summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3kernel
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3kernel')
-rw-r--r--macros/latex/contrib/l3kernel/CHANGELOG.md507
-rw-r--r--macros/latex/contrib/l3kernel/README.md85
-rw-r--r--macros/latex/contrib/l3kernel/expl3.dtx1737
-rw-r--r--macros/latex/contrib/l3kernel/expl3.pdfbin0 -> 585792 bytes
-rw-r--r--macros/latex/contrib/l3kernel/interface3.pdfbin0 -> 1528124 bytes
-rw-r--r--macros/latex/contrib/l3kernel/interface3.tex78
-rw-r--r--macros/latex/contrib/l3kernel/l3.ins164
-rw-r--r--macros/latex/contrib/l3kernel/l3alloc.dtx191
-rw-r--r--macros/latex/contrib/l3kernel/l3basics.dtx3600
-rw-r--r--macros/latex/contrib/l3kernel/l3bootstrap.dtx575
-rw-r--r--macros/latex/contrib/l3kernel/l3box.dtx2279
-rw-r--r--macros/latex/contrib/l3kernel/l3candidates.dtx4480
-rw-r--r--macros/latex/contrib/l3kernel/l3clist.dtx2031
-rw-r--r--macros/latex/contrib/l3kernel/l3coffins.dtx2526
-rw-r--r--macros/latex/contrib/l3kernel/l3color-base.dtx192
-rw-r--r--macros/latex/contrib/l3kernel/l3deprecation.dtx945
-rw-r--r--macros/latex/contrib/l3kernel/l3doc.dtx4598
-rw-r--r--macros/latex/contrib/l3kernel/l3docstrip.dtx292
-rw-r--r--macros/latex/contrib/l3kernel/l3docstrip.pdfbin0 -> 263863 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3expan.dtx2804
-rw-r--r--macros/latex/contrib/l3kernel/l3file.dtx2881
-rw-r--r--macros/latex/contrib/l3kernel/l3final.dtx389
-rw-r--r--macros/latex/contrib/l3kernel/l3flag.dtx335
-rw-r--r--macros/latex/contrib/l3kernel/l3format.ins158
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-assign.dtx229
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-aux.dtx1291
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-basics.dtx2178
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-convert.dtx621
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-expo.dtx1379
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-extended.dtx1273
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-logic.dtx737
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-parse.dtx2908
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-random.dtx664
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-round.dtx592
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-traps.dtx400
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-trig.dtx1698
-rw-r--r--macros/latex/contrib/l3kernel/l3fp.dtx1488
-rw-r--r--macros/latex/contrib/l3kernel/l3fparray.dtx333
-rw-r--r--macros/latex/contrib/l3kernel/l3int.dtx2603
-rw-r--r--macros/latex/contrib/l3kernel/l3intarray.dtx554
-rw-r--r--macros/latex/contrib/l3kernel/l3kernel-functions.dtx642
-rw-r--r--macros/latex/contrib/l3kernel/l3keys.dtx2831
-rw-r--r--macros/latex/contrib/l3kernel/l3legacy.dtx100
-rw-r--r--macros/latex/contrib/l3kernel/l3luatex.dtx539
-rw-r--r--macros/latex/contrib/l3kernel/l3msg.dtx2097
-rw-r--r--macros/latex/contrib/l3kernel/l3names.dtx2064
-rw-r--r--macros/latex/contrib/l3kernel/l3obsolete.txt639
-rw-r--r--macros/latex/contrib/l3kernel/l3prefixes.csv183
-rw-r--r--macros/latex/contrib/l3kernel/l3prefixes.pdfbin0 -> 59016 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3prefixes.tex83
-rw-r--r--macros/latex/contrib/l3kernel/l3prg.dtx1625
-rw-r--r--macros/latex/contrib/l3kernel/l3prop.dtx1262
-rw-r--r--macros/latex/contrib/l3kernel/l3quark.dtx636
-rw-r--r--macros/latex/contrib/l3kernel/l3regex.dtx6807
-rw-r--r--macros/latex/contrib/l3kernel/l3seq.dtx2070
-rw-r--r--macros/latex/contrib/l3kernel/l3skip.dtx2344
-rw-r--r--macros/latex/contrib/l3kernel/l3sort.dtx1103
-rw-r--r--macros/latex/contrib/l3kernel/l3str-convert.dtx3471
-rw-r--r--macros/latex/contrib/l3kernel/l3str.dtx1984
-rw-r--r--macros/latex/contrib/l3kernel/l3styleguide.pdfbin0 -> 280597 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3styleguide.tex303
-rw-r--r--macros/latex/contrib/l3kernel/l3syntax-changes.pdfbin0 -> 228302 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3syntax-changes.tex183
-rw-r--r--macros/latex/contrib/l3kernel/l3sys.dtx327
-rw-r--r--macros/latex/contrib/l3kernel/l3term-glossary.pdfbin0 -> 187099 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3term-glossary.tex68
-rw-r--r--macros/latex/contrib/l3kernel/l3tl-analysis.dtx1106
-rw-r--r--macros/latex/contrib/l3kernel/l3tl.dtx3394
-rw-r--r--macros/latex/contrib/l3kernel/l3token.dtx2505
-rw-r--r--macros/latex/contrib/l3kernel/l3unicode.dtx261
-rw-r--r--macros/latex/contrib/l3kernel/source3.pdfbin0 -> 5707450 bytes
-rw-r--r--macros/latex/contrib/l3kernel/source3.tex98
-rw-r--r--macros/latex/contrib/l3kernel/source3body.tex494
73 files changed, 89014 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/CHANGELOG.md b/macros/latex/contrib/l3kernel/CHANGELOG.md
new file mode 100644
index 0000000000..406ef7bdc1
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/CHANGELOG.md
@@ -0,0 +1,507 @@
+# 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]
+
+## [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 `lstr-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 (fixes #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 (fixes #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 (fixes #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` (fixes #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
+- Cor­rect date string in `ex­pl3.dtx`
+- Cor­rect `\c_sys_en­gine_ver­sion_str` when using XeTeX
+
+## [2018-05-12]
+
+### Added
+- Define `\c_zero_int` and `\c_one_int`
+- Im­ple­ment `\c_sys_en­gine_ver­sion_str`
+- Im­ple­ment `\seq_in­dexed_map_func­tion/in­line`
+- Im­ple­ment `\in­tar­ray_gzero:N`
+- Im­ple­ment `\in­tar­ray_const_from_clist:Nn`
+- Im­ple­ment `\bool_set_in­verse:N`
+- Im­ple­ment `\bool_xor:nnTF` in­stead of just `\bool_xor_p:nn`
+- Im­ple­ment can­di­date `\int_rand:n`
+- Im­ple­ment `\in­tar­ray_gset_rand:Nnn`
+- Im­ple­ment can­di­date `l3f­par­ray` mod­ule
+
+## Changed
+- Up­date min­i­mal re­quired ver­sions of XeTeX and LuaTeX
+- Dep­re­cate named in­te­ger con­stants `\c_zero`. etc.
+- Move all prim­i­tives to `\tex_...:D names­pace`,
+ dep­re­cat­ing older en­gine-de­pen­dent pre­fixes
+- Several internal optimisations
+
+### Fixed
+
+- Ex­pand boolean ex­pres­sion be­fore call­ing `\chardef` (fixes #461)
+
+### Removed
+- Re­move un­doc­u­mented `\fp_func­tion:Nw` and `\fp_new_func­tion: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 (fixes #455)
+- Several performance improvements
+- Documentation improvements
+
+### Fixed
+- Only index TF, T, F functions together if they are `expl3`
+ functions (fixes #453)
+- Make `\infty` and `\pi` into errors in fp expressions
+ (fixes #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 func­tions have been added for dim vari­ables,
+ e.g. `\dim_step_in­line:nnnn`
+
+[Unreleased]: https://github.com/latex3/latex3/compare/2019-08-25...HEAD
+[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/macros/latex/contrib/l3kernel/README.md b/macros/latex/contrib/l3kernel/README.md
new file mode 100644
index 0000000000..1323b1293f
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/README.md
@@ -0,0 +1,85 @@
+LaTeX3 Programming Conventions
+==============================
+
+Release 2019-08-25
+
+Overview
+--------
+
+The files of the `l3kernel` bundle provide a low-level API for TeX programmers
+with special 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 the LaTeX3 kernel and other advanced
+extensions are built. Special care has been taken so that they can be used
+within a LaTeX2e context as regular packages.
+
+While `l3kernel` is still experimental, the bundle is now regarded as broadly
+stable. 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 `l3kernel`.
+
+Programmers making use of `l3kernel` are *strongly* encouraged to subscribe to
+the LaTeX-L mailing list (see below): announcements concerning the deprecation
+or modification of functions are made on the list.
+
+Requirements
+------------
+
+The `l3kernel` bundle requires the e-TeX extensions and additional functionality
+to support string comparisons, expandable character generation with arbitrary
+category codes (for Unicode engines) and PDF support primitives (where direct
+PDF generation is used). The bundle only works with the following engines:
+* pdfTeX v1.40 or later
+* XeTeX v0.99992 or later
+* LuaTeX v0.76 or later
+* e-(u)pTeX from mid-2012 onward
+
+pdfTeX v1.40 was released in 2007, and so any recent TeX distribution
+supports `l3kernel`. Both XeTeX and LuaTeX have developed more
+actively over the past few years, and for this reason only recent
+releases of these engines are supported.
+
+(Engine developers should contact the team for detailed discussion about
+primitive requirements.)
+
+Discussion
+----------
+
+Discussion concerning the approach, suggestions for improvements,
+changes, additions, _etc._ should be addressed to the list
+[LaTeX-L](https://listserv.uni-heidelberg.de/cgi-bin/wa?A0=LATEX-L).
+
+You can subscribe to this list by sending mail to
+
+ listserv@urz.uni-heidelberg.de
+
+with the body containing
+
+ subscribe LATEX-L <Your-First-Name> <Your-Second-Name>
+
+Issues
+------
+
+The issue tracker for LaTeX3 is currently located
+[on GitHub](https://github.com/latex3/latex3/issues).
+
+Please report specific issues with LaTeX3 code there; more general
+discussion should be directed to the [LaTeX-L list](#Discussion).
+
+The LaTeX3 Project
+------------------
+
+Development of LaTeX3 is carried out by
+[The LaTeX3 Project](https://www.latex-project.org/latex3/).
+
+The development team can be contacted
+by e-mail: <latex-team@latex-project.org>; for general LaTeX3 discussion
+the [LaTeX-L list](#Discussion) should be used.
+
+-----
+
+<p>Copyright (C) 1998-2012,2015-2019 The LaTeX3 Project <br />
+<a href="http://latex-project.org/">http://latex-project.org/</a> <br />
+All rights reserved.</p>
diff --git a/macros/latex/contrib/l3kernel/expl3.dtx b/macros/latex/contrib/l3kernel/expl3.dtx
new file mode 100644
index 0000000000..7733d54a7a
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/expl3.dtx
@@ -0,0 +1,1737 @@
+% \iffalse meta-comment
+%
+%% File: expl3.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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>
+\def\ExplFileDate{2019-08-25}%
+%</driver|generic|package>
+%<*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 \textsf{expl3} package and \LaTeX3 programming^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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 \LaTeX3 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 \LaTeX3 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. Private functions should be defined with |__| added to the beginning
+% of the module name. Thus
+% \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.
+%
+% In the same way, private variables should use two "__" at the start of the
+% module name, such that
+% \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{l3docstrip} 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{l3docstrip} 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{l3docstrip} 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: 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[bool] either true or false (the \LaTeX3 implementation does
+% not use \tn{iftrue} or \tn{iffalse});
+% \item[box] box register;
+% \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[ior] an input stream (for reading from a file);
+% \item[iow] an output stream (for writing to a file);
+% \item[int] integer-valued count register;
+% \item[muskip] math mode \enquote{rubber} lengths;
+% \item[prop] property list;
+% \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[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] This means: \textbf{Do not use}. This special case is used
+% for \TeX{} primitives. Programmers outside the kernel team should
+% not use these functions!
+% \item[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 floating points and
+% 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
+% floating points and boxes), and which therefore deliver a single
+% \enquote{value}.
+% \item[x] Fully-expanded token or braced token list.\\
+% This means that the argument is expanded as in the replacement
+% text of an~\tn{edef}, and the expansion is passed to the function as
+% a braced token list. Expansion takes place until only unexpandable
+% tokens are left. |x|-type arguments cannot be nested.
+% \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 |x| 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.
+% \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:cnx|, 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 { cnx }
+% \end{verbatim}
+% This defines the variant form so that you can then write, for example:
+% \begin{verbatim}
+% \demo_cmd:cnx { 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:xcxcx|, 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 { xcxcx }
+% \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}
+%
+% At present, the \pkg{expl3} modules are designed to be loaded on top
+% of \LaTeXe{}. In time, a \LaTeX3 format will be produced based on this
+% code. This allows the code to be used in \LaTeXe{} packages \emph{now}
+% while a stand-alone \LaTeX3 is developed.
+%
+% \begin{bfseries}
+% While \pkg{expl3} is still experimental, the bundle is now regarded
+% as broadly stable. 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}.
+% \end{bfseries}
+%
+% New modules will be added to the distributed version of \pkg{expl3}
+% as they reach maturity. At present, the \pkg{expl3} bundle consists
+% of a number of modules, most of which are loaded by including the
+% line:
+% \begin{verbatim}
+% \RequirePackage{expl3}
+% \end{verbatim}
+% in a \LaTeXe{} package, class or other file. The \pkg{expl3} modules
+% regarded as stable, and therefore suitable for basing real code on,
+% are as follows:
+%
+% \begin{description}
+% \providecommand\explpkg[2]{\item[#1]#2}
+% \explpkg{l3basics}{
+% This contains the basic definition modules used
+% by the other packages.
+% }
+% \explpkg{l3box}{
+% Primitives for dealing with boxes.
+% }
+% \explpkg{l3clist}{
+% Methods for manipulating comma-separated token lists.
+% }
+% \explpkg{l3coffins}{
+% Augmented box constructs for alignment operations.
+% }
+% \explpkg{l3expan}{
+% This is the argument expansion module discussed earlier in this
+% document.
+% }
+% \explpkg{l3int}{
+% This implements the integer data-type \texttt{int}.
+% }
+% \explpkg{l3keys}{
+% For processing lists of the form
+% \texttt{\{ key1=val1 , key2=val2 \}}, intended to work
+% as a \LaTeX3 version of \pkg{xkeyval}/\pkg{kvoptions}, although
+% with input syntax more like that of \pkg{pgfkeys}.
+% }
+% \explpkg{l3msg}{
+% Communicating with the user: includes low-level hooks to allow
+% messages to be filtered (higher-level interface for filtering
+% to be written!).
+% }
+% \explpkg{l3names}{
+% This sets up the basic naming scheme and renames all
+% the \TeX{} primitives.
+% }
+% \explpkg{l3prg}{
+% Program control structures such as boolean data type |bool|, generic
+% do-while loops, and conditional flow.
+% }
+% \explpkg{l3prop}{
+% This implements the data-type for \enquote{property lists} that are used, in
+% particular, for storing key/value pairs.
+% }
+% \explpkg{l3quark}{
+% A \enquote{quark} is a command that is defined to expand to
+% itself! Therefore they must never be expanded as this would generate an
+% infinite recursion; they do however have many uses, \emph{e.g.}~as
+% special markers and delimiters within code.
+% }
+% \explpkg{l3seq}{
+% This implements data-types such as queues and stacks.
+% }
+% \explpkg{l3skip}{
+% Implements the \enquote{rubber length} datatype \texttt{skip}, the
+% \enquote{rigid length} datatype |dim|, and the math mode
+% \enquote{rubber length} datatype \texttt{muskip}.
+% }
+% \explpkg{l3tl}{
+% This implements a basic data-type, called a \textit{token-list
+% variable} (|tl var.|), used for storing named token lists: these are
+% \TeX{} macros with no arguments.
+% }
+% \explpkg{l3token}{
+% Analysing token lists and token streams, including peeking ahead to
+% see what's coming next and inspecting tokens to detect which kind
+% they are.
+% }
+% \end{description}
+%
+% \section{Moving from \LaTeXe{} to \LaTeX3}
+%
+% To help programmers to use \LaTeX3 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{@ifpackageloaded}, as there
+% is currently no native \LaTeX3 package loading module.
+% \item User level macros should be generated using the mechanism
+% available in the \pkg{xparse} package, which is part of the
+% \texttt{l3package} bundle, available from CTAN or the \LaTeX3 SVN
+% repository.
+% \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:N(TF)} 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{undo-recent-deprecations}
+% The \texttt{undo-recent-deprecations} option suppresses deprecation
+% errors for the first six months after a command is deprecated. It is
+% intended as a last resort measure for users of packages that were not
+% updated in time.
+%
+% \DescribeOption{check-declarations}
+% All variables used in \LaTeX3 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{l3candidates} 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 for \LaTeX3). See also \cs{debug_on:n}
+% \texttt{\{log-functions\}} in \pkg{l3candidates} for finer control.
+%
+% \DescribeOption{enable-debug}
+% To allow more localized checking and logging than provided by
+% \texttt{check-declarations} and \texttt{log-functions}, \pkg{expl3}
+% provides a few \cs[no-index]{debug_\ldots{}} functions (described
+% elsewhere) that turn on the corresponding checks within a group.
+% These functions can only be used if \pkg{expl3} is loaded with the
+% \texttt{enable-debug} option.
+%
+% \DescribeOption{driver}
+% Selects the driver to be used for color, graphics and related operations that
+% are driver-dependent. Options available are
+% \begin{itemize}[font = \texttt]
+% \item[auto] Let \LaTeX3 determine the correct driver. With DVI output, this
+% selects the \texttt{dvips} back-end for \pdfTeX{} and \LuaTeX{}, and
+% \texttt{dvipdfmx} for \pTeX{} and \upTeX{}. This is the standard setting.
+% \item[latex2e] Use the \pkg{graphics} package to select the driver, rather
+% than \LaTeX3 code.
+% \item[dvips] Use the \texttt{dvips} driver.
+% \item[dvipdfmx] Use the \texttt{dvipdfmx} driver.
+% \item[dvisvgm] Use the \texttt{dvisvgm} driver.
+% \item[pdfmode] Use the \texttt{pdfmode} driver (direct PDF output from
+% \pdfTeX{} or \LuaTeX{}).
+% \item[xdvipdfmx] Use the \texttt{xdvipdfmx} driver (\XeTeX{} only).
+% \end{itemize}
+%
+% \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.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{Engine/primitive requirements}
+%
+% To use \pkg{expl3} and the higher level packages provided by the
+% team, the minimal set of primitive requirements is currently
+% \begin{itemize}
+% \item All of those from \TeX90.
+% \item All of those from \eTeX{} \emph{excluding} |\TeXXeTstate|,
+% |\beginL|, |\beginR|, |\endL| and |\endR| (\emph{i.e.}~excluding
+% \TeX{}-\kern0pt-\reflectbox{\TeX{}}).
+% \item Functionality equivalent to the \pdfTeX{} primitive
+% |\pdfstrcmp|.
+% \end{itemize}
+% Any engine which defines |\pdfoutput| (\emph{i.e.}~allows direct production
+% of a PDF file without a DVI intermediate) must also provide |\pdfcolorstack|,
+% |\pdfliteral|, |\pdfmatrix|, |\pdfrestore| and |\pdfsave| or equivalent
+% functionality. Fully Unicode engines must provide a method for producing
+% character tokens in an expandable manner.
+%
+% Practically, these requirements are met by the engines
+% \begin{itemize}
+% \item \pdfTeX{} v1.40 or later.
+% \item \XeTeX{} v0.99992 or later.
+% \item \LuaTeX{} v0.76 or later.
+% \item e-(u)\pTeX{} mid-2012 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 \LaTeX3 Project}
+%
+% Development of \LaTeX3 is carried out by The \LaTeX3 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 \LaTeX3 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}
+%
+% 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{} loader}
+%
+% \begin{macrocode}
+%<*package&loader>
+% \end{macrocode}
+%
+% Identify the package.
+% \begin{macrocode}
+\ProvidesPackage{expl3}
+ [%
+ \ExplFileDate\space
+ L3 programming layer (loader)
+ ]%
+% \end{macrocode}
+%
+% \begin{macro}[int]{\expl@create@bool@option}
+% \begin{variable}[int]
+% {
+% \l@expl@undo@recent@deprecations@bool ,
+% \l@expl@check@declarations@bool ,
+% \l@expl@log@functions@bool ,
+% \l@expl@enable@debug@bool
+% }
+% \begin{variable}[int]{\l@expl@options@clist}
+% Options to be set up. These have to be done by hand as there is no
+% \pkg{expl3} yet: the logging option is needed before loading
+% \pkg{l3basics}! Only a minimal set of options are handled here:
+% others are left for a proper key--value approach once the kernel is
+% loaded. After processing the options we set the
+% \texttt{enable-debug} boolean so that the options
+% \texttt{check-declarations} and \texttt{log-functions} imply
+% \texttt{enable-debug}: otherwise the necessary debugging code would
+% not be set up properly.
+% \begin{macrocode}
+\newcommand\expl@create@bool@option[2]%
+ {%
+ \DeclareOption{#1}{\chardef #2=1 }%
+ \DeclareOption{#1=true}{\chardef #2=1 }%
+ \DeclareOption{#1=false}{\chardef #2=0 }%
+ \newcommand*#2{}%
+ \chardef #2=0 %
+ }%
+\expl@create@bool@option
+ {undo-recent-deprecations}\l@expl@undo@recent@deprecations@bool
+\expl@create@bool@option{check-declarations}\l@expl@check@declarations@bool
+\expl@create@bool@option{log-functions}\l@expl@log@functions@bool
+\expl@create@bool@option{enable-debug}\l@expl@enable@debug@bool
+\let\expl@create@bool@option\@undefined
+\chardef\l@expl@enable@debug@bool=0 %
+\newcommand*\l@expl@options@clist{}%
+\DeclareOption*
+ {%
+ \ifx\l@expl@options@clist\@empty
+ \let\l@expl@options@clist\CurrentOption
+ \else
+ \expandafter\expandafter\expandafter\def
+ \expandafter\expandafter\expandafter\l@expl@options@clist
+ \expandafter\expandafter\expandafter
+ {\expandafter\l@expl@options@clist\expandafter,\CurrentOption}
+ \fi
+ }%
+\ProcessOptions\relax
+\ifodd\l@expl@check@declarations@bool
+ \chardef\l@expl@enable@debug@bool=1 %
+\fi
+\ifodd\l@expl@log@functions@bool
+ \chardef\l@expl@enable@debug@bool=1 %
+\fi
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}{\ProvidesExplPackage, \ProvidesExplClass, \ProvidesExplFile}
+% For other packages and classes building on this one it is convenient
+% not to need \cs{ExplSyntaxOn} each time.
+% \begin{macrocode}
+\protected\def\ProvidesExplPackage#1#2#3#4%
+ {%
+ \ProvidesPackage{#1}[#2 \ifx\relax#3\relax\else v#3\space\fi #4]%
+ \ExplSyntaxOn
+ }%
+\protected\def\ProvidesExplClass#1#2#3#4%
+ {%
+ \ProvidesClass{#1}[#2 \ifx\relax#3\relax\else v#3\space\fi #4]%
+ \ExplSyntaxOn
+ }%
+\protected\def\ProvidesExplFile#1#2#3#4%
+ {%
+ \ProvidesFile{#1}[#2 \ifx\relax#3\relax\else v#3\space\fi #4]%
+ \ExplSyntaxOn
+ }%
+% \end{macrocode}
+% \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{variable}{\l__expl_driver_tl}
+% \begin{variable}{\l__expl_native_drivers_bool}
+% With the code now loaded, options can be handled using a real key--value
+% interpreter. The \enquote{faked} options are also included so that any
+% erroneous input is mopped up (\emph{e.g.}~\texttt{log-function =
+% foo}). The checks on driver choice are set up here, so when actually
+% the driver it's a straight forward operation.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { expl } { wrong-driver }
+ { Driver~request~inconsistent~with~engine:~using~'#2'~driver. }
+ {
+ You~have~requested~driver~'#1',~but~this~is~not~suitable~for~use~with~the~
+ active~engine.~LaTeX3~will~use~the~'#2'~driver~instead.
+ }
+\tl_new:N \l__expl_driver_tl
+\keys_define:nn { expl }
+ {
+ driver .choice:,
+ driver / auto .code:n =
+ {
+ \tl_set:Nx \l__expl_driver_tl
+ {
+ \sys_if_engine_xetex:TF
+ { xdvipdfmx }
+ {
+ \sys_if_output_pdf:TF
+ { pdfmode }
+ {
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_pdftex_p: }
+ { \sys_if_engine_luatex_p: }
+ { dvips }
+ { dvipdfmx }
+ }
+ }
+ }
+ },
+ driver / dvipdfmx .code:n =
+ {
+ \tl_set:Nn \l__expl_driver_tl { dvipdfmx }
+ \sys_if_engine_xetex:TF
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvipdfmx } { xdvipdfmx }
+ \tl_set:Nn \l__expl_driver_tl { xdvipdfmx }
+ }
+ {
+ \sys_if_output_pdf:T
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvipdfmx } { pdfmode }
+ \tl_set:Nn \l__expl_driver_tl { pdfmode }
+ }
+ }
+ },
+ driver / dvips .code:n =
+ {
+ \tl_set:Nn \l__expl_driver_tl { dvips }
+ \sys_if_engine_xetex:TF
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvips } { xdvipdfmx }
+ \tl_set:Nn \l__expl_driver_tl { xdvipdfmx }
+ }
+ {
+ \sys_if_output_pdf:T
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvips } { pdfmode }
+ \tl_set:Nn \l__expl_driver_tl { pdfmode }
+ }
+ }
+ },
+ driver / dvisvgm .code:n =
+ {
+ \tl_set:Nn \l__expl_driver_tl { dvisvgm }
+ \sys_if_engine_xetex:TF
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvips } { xdvipdfmx }
+ \tl_set:Nn \l__expl_driver_tl { xdvipdfmx }
+ }
+ {
+ \sys_if_output_pdf:T
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { dvips } { pdfmode }
+ \tl_set:Nn \l__expl_driver_tl { pdfmode }
+ }
+ }
+ },
+ driver / latex2e .code:n =
+ { \tl_set:Nn \l__expl_driver_tl { latex2e } },
+ driver / pdfmode .code:n =
+ {
+ \tl_set:Nn \l__expl_driver_tl { pdfmode }
+ \sys_if_engine_xetex:TF
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { pdfmode } { xdvipdfmx }
+ \tl_set:Nn \l__expl_driver_tl { xdvipdfmx }
+ }
+ {
+ \sys_if_output_pdf:F
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { pdfmode } { dvips }
+ \tl_set:Nn \l__expl_driver_tl { dvips }
+ }
+ }
+ },
+ driver / xdvipdfmx .code:n =
+ {
+ \tl_set:Nn \l__expl_driver_tl { xdvipdfmx }
+ \sys_if_engine_xetex:F
+ {
+ \sys_if_output_pdf:TF
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { xdvipdfmx } { pdfmode }
+ \tl_set:Nn \l__expl_driver_tl { pdfmode }
+ }
+ {
+ \__kernel_msg_error:nnnn { expl } { wrong-driver }
+ { xdvipdfmx } { dvips }
+ \tl_set:Nn \l__expl_driver_tl { dvips }
+ }
+ }
+ },
+ driver .initial:n = { auto } ,
+ native-drivers .choice:,
+ native-drivers .default:n = { true },
+ native-drivers / false .meta:n = { driver = latex2e },
+ native-drivers / true .meta:n = { driver = auto }
+ }
+% \end{macrocode}
+% Mop up any incorrect settings for the other options.
+% \begin{macrocode}
+\keys_define:nn { expl }
+ {
+ undo-recent-deprecations .bool_set:N = \l@expl@undo@recent@deprecations@bool,
+ check-declarations .bool_set:N = \l@expl@check@declarations@bool,
+ log-functions .bool_set:N = \l@expl@log@functions@bool
+ }
+\keys_set:nV { expl } \l@expl@options@clist
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \box_rotate:Nn,
+% \box_resize_to_wd_and_ht_plus_dp:Nnn,
+% \box_resize_to_ht_plus_dp:Nn,
+% \box_resize_to_wd:Nn,
+% \box_scale:Nnn
+% }
+% For the \LaTeXe{} drivers,
+% alter various definitions to use the \pkg{graphics} package
+% instead. The package is loaded right at the start of the hook
+% as there is otherwise a potential issue with \pkg{(x)color}: see
+% \url{http://groups.google.com/group/comp.text.tex/msg/c9de8913c756ef4c}.
+% \begin{macrocode}
+\str_if_eq:VnTF \l__expl_driver_tl { latex2e }
+ {
+ \tl_gput_left:Nn \@begindocumenthook { \RequirePackage { graphics } }
+ \__kernel_msg_new:nnnn { box } { clipping-not-available }
+ { Box~clipping~not~available. }
+ {
+ The~\box_clip:N~function~is~only~available~when~loading~expl3~
+ with~the~"native-drivers"~option.
+ }
+ \cs_set_protected:Npn \box_clip:N #1
+ {
+ \hbox_set:Nn #1 { \box_use:N #1 }
+ \__kernel_msg_error:nn { box } { clipping-not-available }
+ }
+ \cs_set_protected:Npn \box_rotate:Nn #1#2
+ { \hbox_set:Nn #1 { \rotatebox {#2} { \box_use:N #1 } } }
+ \cs_set_protected:Npn \box_resize_to_wd_and_ht_plus_dp:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox *
+ { \dim_eval:n {#2} }
+ { \dim_eval:n {#3} }
+ { \box_use:N #1 }
+ }
+ }
+ \cs_set_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox * { ! } { \dim_eval:n {#2} }
+ { \box_use:N #1 }
+ }
+ }
+ \cs_set_protected:Npn \box_resize_to_wd:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox * { \dim_eval:n {#2} } { ! }
+ { \box_use:N #1 }
+ }
+ }
+ \cs_set_protected:Npn \__box_scale_aux:N #1
+ {
+ \exp_last_unbraced:Nx \scalebox
+ { { \fp_use:N \l__box_scale_x_fp } [ \fp_use:N \l__box_scale_y_fp ] }
+ { \box_use:N #1 }
+ }
+ \cs_set_protected:Npn \color_ensure_current: { \set@color }
+ }
+% \end{macrocode}
+% \end{macro}
+% For native drivers, just load the appropriate file. As \cs{expl3} syntax
+% is already on and the full mechanism is only engaged at the end of the
+% loader, \cs{ProvidesExplFile} is temporarily redefined here.
+% \begin{macrocode}
+ {
+ \group_begin:
+ \cs_set_protected:Npn \ProvidesExplFile
+ {
+ \char_set_catcode_space:n { `\ }
+ \ProvidesExplFileAux
+ }
+ \cs_set_protected:Npn \ProvidesExplFileAux #1#2#3#4
+ {
+ \group_end:
+ \ProvidesFile {#1} [ #2~v#3~#4 ]
+ }
+ \tl_const:Nn \c__expl_def_ext_tl { def }
+ \@onefilewithoptions { l3backend- \l__expl_driver_tl } [ ] [ ]
+ \c__expl_def_ext_tl
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\@pushfilename, \@popfilename}
+% \begin{macro}{\__expl_status_pop:w}
+% 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.
+% \begin{macrocode}
+\tl_put_left:Nn \@pushfilename
+ {
+ \exp_args:Nx \__kernel_file_input_push:n
+ {
+ \tl_to_str:N \@currname
+ \tl_to_str:N \@currext
+ }
+ \tl_put_left:Nx \l__expl_status_stack_tl
+ {
+ \bool_if:NTF \l__kernel_expl_bool
+ { 1 }
+ { 0 }
+ }
+ \ExplSyntaxOff
+ }
+\tl_put_right:Nn \@pushfilename { \@pushfilenameaux }
+% \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_set_protected:Npn \@pushfilenameaux #1#2#3
+ {
+ \str_gset:Nn \g_file_curr_name_str {#3}
+ #1 #2 {#3}
+ }
+\tl_put_right:Nn \@popfilename
+ {
+ \__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 \q_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_new_protected:Npn \__expl_status_pop:w #1#2 \q_stop
+ {
+ \tl_set:Nn \l__expl_status_stack_tl {#2}
+ \int_if_odd:nTF {#1}
+ { \ExplSyntaxOn }
+ { \ExplSyntaxOff }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l__expl_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_new:N \l__expl_status_stack_tl
+\tl_set:Nn \l__expl_status_stack_tl { 0 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</package&loader>
+% \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{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{variable}[int]
+% {
+% \l@expl@undo@recent@deprecations@bool ,
+% \l@expl@check@declarations@bool ,
+% \l@expl@log@functions@bool ,
+% \l@expl@enable@debug@bool
+% }
+% In generic mode, there is no convenient option handling and so instead
+% the two variables are defined to do nothing.
+% Appropriate value before input of the loader.
+% \begin{macrocode}
+\chardef \l@expl@undo@recent@deprecations@bool = 0 %
+\chardef \l@expl@check@declarations@bool = 0 %
+\chardef \l@expl@log@functions@bool = 0 %
+\chardef \l@expl@enable@debug@bool = 0 %
+% \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 nothing for these. 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{}%
+\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}
+%
+% For driver loading in generic mode, there are no options: pick the most
+% appropriate case! To allow this loading to take place a temporary
+% definition of \cs{ProvidesExplFile} is provided
+% \begin{macrocode}
+\cs_set_protected:Npn \ProvidesExplFile
+ {
+ \group_begin:
+ \char_set_catcode_space:n { `\ }
+ \ProvidesExplFileAux
+ }
+\cs_set_protected:Npn \ProvidesExplFileAux #1#2#3#4
+ {
+ \group_end:
+ \iow_log:x { File:~#1~#2~v#3~#4 }
+ }
+\tex_input:D
+ l3backend-
+ \sys_if_engine_xetex:TF
+ { xdvipdfmx }
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:N \tex_pdfoutput:D }
+ { \int_compare_p:nNn \tex_pdfoutput:D > 0 }
+ { pdfmode }
+ { dvips }
+ }
+ .def \scan_stop:
+\cs_undefine:N \ProvidesExplFile
+\cs_undefine:N \ProvidesExplFileAux
+% \end{macrocode}
+%
+% 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/macros/latex/contrib/l3kernel/expl3.pdf b/macros/latex/contrib/l3kernel/expl3.pdf
new file mode 100644
index 0000000000..85c664166d
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/expl3.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/interface3.pdf b/macros/latex/contrib/l3kernel/interface3.pdf
new file mode 100644
index 0000000000..70ff745abf
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/interface3.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/interface3.tex b/macros/latex/contrib/l3kernel/interface3.tex
new file mode 100644
index 0000000000..d073bc8574
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/interface3.tex
@@ -0,0 +1,78 @@
+\iffalse meta-comment
+
+File: interfaces3.tex
+
+Copyright (C) 1990-2011,2017-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 170 pages currently).
+%
+% 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}
+\listfiles
+
+\begin{document}
+
+\title{The \LaTeX3 Interfaces}
+\author{%
+ The \LaTeX3 Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2019-08-25}
+
+\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/macros/latex/contrib/l3kernel/l3.ins b/macros/latex/contrib/l3kernel/l3.ins
new file mode 100644
index 0000000000..8affab2c2d
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3.ins
@@ -0,0 +1,164 @@
+\iffalse meta-comment
+
+File l3.ins
+
+Copyright (C) 2011,2012,2014-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 l3docstrip.dtx
+\askforoverwritefalse
+
+\preamble
+
+Copyright (C) 1990-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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}
+ {
+ % ======== PACKAGE ONLY ========
+ \from{expl3.dtx} {package}
+ % ==============================
+ \from{l3bootstrap.dtx} {package}
+ \from{l3names.dtx} {package}
+ \from{l3basics.dtx} {package}
+ \from{l3expan.dtx} {package}
+ \from{l3tl.dtx} {package}
+ \from{l3str.dtx} {package}
+ \from{l3quark.dtx} {package}
+ \from{l3seq.dtx} {package}
+ \from{l3int.dtx} {package}
+ \from{l3flag.dtx} {package}
+ \from{l3prg.dtx} {package}
+ \from{l3sys.dtx} {package}
+ \from{l3clist.dtx} {package}
+ \from{l3token.dtx} {package}
+ \from{l3prop.dtx} {package}
+ \from{l3msg.dtx} {package}
+ \from{l3file.dtx} {package}
+ \from{l3skip.dtx} {package}
+ \from{l3keys.dtx} {package}
+ \from{l3intarray.dtx} {package}
+ \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{l3fparray.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-base.dtx} {package}
+ \from{l3coffins.dtx} {package}
+ \from{l3luatex.dtx} {package,tex}
+ \from{l3unicode.dtx} {package}
+ \from{l3candidates.dtx} {package}
+ \from{l3legacy.dtx} {package}
+ \from{l3deprecation.dtx}{package}
+ }
+ }
+
+\generate{\file{expl3.sty} {\from{expl3.dtx} {package,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{l3docstrip.tex} {\from{l3docstrip.dtx} {program}}}
+
+\generate{\file{l3names.def} {\from{l3names.dtx} {names}}}
+
+% Lua code
+
+\def\MetaPrefix{--}
+\preamble
+
+Copyright (C) 1990-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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}}}
+
+\endbatchfile
diff --git a/macros/latex/contrib/l3kernel/l3alloc.dtx b/macros/latex/contrib/l3kernel/l3alloc.dtx
new file mode 100644
index 0000000000..1c74174aeb
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3alloc.dtx
@@ -0,0 +1,191 @@
+% \iffalse meta-comment
+%
+%% File: l3alloc.dtx
+%
+% Copyright (C) 1990-2012,2014-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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{l3alloc} package\\ Register allocation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides the basic mechanism for allocating \TeX{}'s
+% registers when operating in format mode. When loaded as a package on
+% an existing format, the mechanisms from the latter are used.
+%
+% The approach used here is informed by the mechanisms used in plain
+% \TeX{}/\LaTeX{} but noting that newer engines provide us much more
+% flexibility. In addition to this, we do not need allocators for all
+% register types: for example \texttt{toks} are not used by \LaTeX3 whilst
+% reads/writes are handled using a pool and thus do not need a classical
+% allocator.
+%
+% In classical (non-\LuaTeX{}) engines, there are various restriction on
+% registers, for example |\box255| is hard-coded as the output box and
+% inserts have to be allocated below this (not in the extended pool). Rather
+% than worry about this, allocations for the registers affected (\TeX{}
+% |box|, |count|, |dimen| and |skip| types) are simply made only from the
+% extended pool. (There is a performance impact in engines other than
+% \LuaTeX{} but the free use of registers in \pkg{expl3} means that code
+% cannot be sure of obtaining a low-numbered register in any case.)
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3alloc} implementation}
+%
+% \begin{macrocode}
+%<*initex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=alloc>
+% \end{macrocode}
+%
+% \begin{variable}
+% {
+% \g_@@_int_int ,
+% \g_@@_dim_int ,
+% \g_@@_muskip_int ,
+% \g_@@_int_int ,
+% \g_@@_box_int
+% }
+% The core register tracking is done using the same raw \TeX{} count
+% registers as reserved by plain \TeX{} and \LaTeXe{}, as there may be the
+% odd piece of generic code that needs to work by number. However, as not
+% all of our variables work the same way, some of the older registers are
+% simply ignored. For the same reason, there is no special status for the
+% low-numbers registers other than counts. To avoid having to worry about
+% inserts and reflecting the register availability in \eTeX{}, the lower
+% register space is unused here (though is available for hard-coded
+% use).
+% \begin{macrocode}
+\tex_countdef:D \g_@@_int_int = 10 ~
+\tex_countdef:D \g_@@_dim_int = 11 ~
+\tex_countdef:D \g_@@_skip_int = 12 ~
+\tex_countdef:D \g_@@_muskip_int = 13 ~
+\tex_countdef:D \g_@@_box_int = 14 ~
+\g_@@_int_int = 255 ~
+\g_@@_dim_int = 255 ~
+\g_@@_skip_int = 255 ~
+\g_@@_muskip_int = 0 ~
+\g_@@_box_int = 255 ~
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_@@_attribute_int ,
+% \g_@@_bytecode_int ,
+% \g_@@_chunkname_int ,
+% \g_@@_whatsit_int
+% }
+% To allow \LuaTeX{} to load |ltluatex.lua| for generic \Lua{} support, a
+% small number of counts have to be correctly named at the \TeX{} level.
+% At present there are no \pkg{expl3} allocators for these concepts so the
+% names and numbers of the tracking variables may change.
+% \begin{macrocode}
+\tex_ifdefined:D \tex_luatexversion:D
+ \tex_global:D \tex_countdef:D \g_@@_attribute_int = 21 ~
+ \tex_global:D \tex_countdef:D \g_@@_bytecode_int = 22 ~
+ \tex_global:D \tex_countdef:D \g_@@_chunkname_int = 23 ~
+ \tex_global:D \tex_countdef:D \g_@@_whatsit_int = 24 ~
+\tex_fi:D
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {\box_new:N, \dim_new:N, \int_new:N, \muskip_new:N \skip_new:N}
+% Each of the public allocators is a wrapper around the one internal
+% function needed here.
+% \begin{macrocode}
+\cs_new_protected:Npx \box_new:N #1
+ {
+ \exp_not:N \@@_reg:nNnN { box }
+ \cs_if_exist:NTF \tex_luatexversion:D
+ { \tex_chardef:D }
+ { \tex_mathchardef:D }
+ \c_max_register_int
+ #1
+ }
+\cs_new_protected:Npn \dim_new:N #1
+ { \@@_reg:nNnN { dim } \tex_dimendef:D \c_max_register_int #1 }
+\cs_new_protected:Npn \int_new:N #1
+ { \@@_reg:nNnN { int } \tex_countdef:D \c_max_register_int #1 }
+\cs_new_protected:Npn \muskip_new:N #1
+ { \@@_reg:nNnN { muskip } \tex_muskipdef:D \c_max_register_int #1 }
+\cs_new_protected:Npn \skip_new:N #1
+ { \@@_reg:nNnN { skip } \tex_skipdef:D \c_max_register_int #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_reg:nNNN}
+% The allocator itself is modelled somewhat on \LaTeXe{}'s \tn{e@alloc},
+% though there is no need to set \tn{allocationnumber}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_reg:nNnN #1#2#3#4
+ {
+ \__kernel_chk_if_free_cs:N #4
+ \int_compare:nNnTF { \int_use:c { g_@@_ #1 _int } } < {#3}
+ {
+ \int_gincr:c { g_@@_ #1 _int }
+ \tex_global:D #2 #4 \int_use:c { g_@@_ #1 _int }
+ \iow_log:x
+ {
+ \token_to_str:N #4 ~=~ #1 ~register~
+ \int_use:c { g_@@_ #1 _int }
+ }
+ }
+ { \__kernel_msg_fatal:nnx { kernel } { out-of-registers } {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3basics.dtx b/macros/latex/contrib/l3kernel/l3basics.dtx
new file mode 100644
index 0000000000..8bd1bd8e6c
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3basics.dtx
@@ -0,0 +1,3600 @@
+% \iffalse meta-comment
+%
+%% File: l3basics.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Basic definitions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% As the name suggest this package holds some basic definitions which
+% are needed by most or all other packages in this set.
+%
+% Here we describe those functions that are used all over the place. With
+% 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.
+% \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.
+% \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{x} expansion. In contrast, \enquote{protected}
+% functions are not expanded within \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{x}-type or \texttt{e}-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: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: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: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{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: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{x}-type or \texttt{e}-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: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: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: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{x}-type or \texttt{e}-type argument.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_set_protected_nopar:Npn, \cs_set_protected_nopar:cpn ,
+% \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{x}-type or \texttt{e}-type argument.
+% \end{function}
+%
+% \begin{function}{\cs_gset:Npn, \cs_gset:cpn, \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: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: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{x}-type or \texttt{e}-type argument.
+% \end{function}
+%
+% \begin{function}
+% {
+% \cs_gset_protected_nopar:Npn, \cs_gset_protected_nopar:cpn,
+% \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{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:Nx, \cs_new:cx
+% }
+% \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:Nx, \cs_new_nopar:cx
+% }
+% \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:Nx, \cs_new_protected:cx
+% }
+% \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{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:Nx, \cs_new_protected_nopar:cx
+% }
+% \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{x}-type or \texttt{e}-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:Nx, \cs_set:cx
+% }
+% \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:Nx, \cs_set_nopar:cx
+% }
+% \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:Nx, \cs_set_protected:cx
+% }
+% \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{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:Nx, \cs_set_protected_nopar:cx
+% }
+% \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{x}-type or \texttt{e}-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:Nx, \cs_gset:cx
+% }
+% \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:Nx, \cs_gset_nopar:cx
+% }
+% \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:Nx, \cs_gset_protected:cx
+% }
+% \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{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:Nx, \cs_gset_protected_nopar:cx
+% }
+% \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{x}-type or \texttt{e}-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: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 \TeX{}'s \tn{meaning} primitive.
+% 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).
+% \begin{texnote}
+% Protected macros that appear in a \texttt{c}-type argument are
+% expanded despite being protected; \cs{exp_not:n} also has no
+% effect. An internal error occurs if non-characters or active
+% characters remain after full expansion, as the conversion to a
+% control sequence is not possible.
+% \end{texnote}
+% \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{x}-type or \texttt{e}-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 = 2019-02-27]{\cs_argument_spec:N}
+% \begin{syntax}
+% \cs{cs_argument_spec:N} \meta{token}
+% \end{syntax}
+% If the \meta{token} is a macro, this function leaves the primitive
+% \TeX{} argument 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_argument_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 argument 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}
+% \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 argument 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}
+% \begin{syntax}
+% \cs{use_i:nn} \Arg{arg_1} \Arg{arg_2}
+% \end{syntax}
+% These functions absorb two arguments from the input stream. The function
+% \cs{use_i:nn} discards the second argument, and leaves the content
+% of the first argument in the input stream. \cs{use_ii:nn} discards
+% the first argument and leaves the content of the second 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.
+% \begin{texnote}
+% These are equivalent to \LaTeXe{}'s \tn{@firstoftwo} and
+% \tn{@secondoftwo}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\use_i:nnn, \use_ii:nnn, \use_iii:nnn}
+% \begin{syntax}
+% \cs{use_i:nnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3}
+% \end{syntax}
+% These functions absorb three arguments from the input stream. The
+% function \cs{use_i:nnn} discards the second and third arguments, and
+% leaves the content of the first argument in the input stream.
+% \cs{use_ii:nnn} and \cs{use_iii:nnn} work similarly, leaving the
+% content of second or third arguments in the input stream, respectively.
+% 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:nnnn, \use_ii:nnnn, \use_iii:nnnn, \use_iv:nnnn}
+% \begin{syntax}
+% \cs{use_i:nnnn} \Arg{arg_1} \Arg{arg_2} \Arg{arg_3} \Arg{arg_4}
+% \end{syntax}
+% These functions absorb four arguments from the input stream. The
+% function \cs{use_i:nnnn} discards the second, third and fourth
+% arguments, and leaves the content of the first argument in the input
+% stream.
+% \cs{use_ii:nnnn}, \cs{use_iii:nnnn} and \cs{use_iv:nnnn} work similarly,
+% leaving the content of second, third or fourth arguments in the input
+% stream, respectively. 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:nnn} \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{@gobbbletwo},
+% \emph{etc.}
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-06-18]{\use:e}
+% \begin{syntax}
+% \cs{use:e} \Arg{expandable tokens}
+% \end{syntax}
+% Fully expands the \meta{token list} in an \texttt{x}-type manner,
+% \emph{but} the function remains fully expandable, and parameter
+% character (usually~|#|) need not be doubled.
+% \begin{texnote}
+% \cs{use:e} is a wrapper around the primitive \tn{expanded}
+% where it is available:
+% it requires two expansions to complete its action.
+% When \tn{expanded} is not available this function is very slow.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2011-12-31]{\use:x}
+% \begin{syntax}
+% \cs{use:x} \Arg{expandable tokens}
+% \end{syntax}
+% Fully expands the \meta{expandable tokens} and inserts the
+% result into the input stream at the current location.
+% Any hash characters (|#|) in the argument must be doubled.
+% \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} form 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} form 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}
+%
+% \begin{variable}{\c_true_bool, \c_false_bool}
+% Constants that represent |true| and |false|, respectively. Used to
+% implement predicates.
+% \end{variable}
+%
+% \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:N}).
+% \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_|.
+%
+% \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}
+% These are equivalent to their corresponding \TeX\ primitive
+% conditionals; \cs{reverse_if:N} is \eTeX's \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 \TeX{}'s \tn{ifx}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\if:w, \if_charcode:w, \if_catcode:w}
+% \begin{syntax}
+% "\if:w" <token_1> <token_2> <true code> "\else:" <false code> "\fi:" \\
+% "\if_catcode:w" <token_1> <token_2> <true code> "\else:" <false code> "\fi:"
+% \end{syntax}
+% These conditionals expand any following tokens until two
+% unexpandable tokens are left. If you wish to prevent this expansion,
+% prefix the token in question with "\exp_not:N". "\if_catcode:w"
+% tests if the category codes of the two tokens are the same whereas
+% "\if:w" tests if the character codes are
+% identical. "\if_charcode:w" is an alternative name for "\if:w".
+% \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.
+% \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.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3basics} implementation}
+%
+% \begin{macrocode}
+%<*initex|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_let:D \if_true: \tex_iftrue:D
+\tex_let:D \if_false: \tex_iffalse:D
+\tex_let:D \or: \tex_or:D
+\tex_let:D \else: \tex_else:D
+\tex_let:D \fi: \tex_fi:D
+\tex_let:D \reverse_if:N \tex_unless:D
+\tex_let:D \if:w \tex_if:D
+\tex_let:D \if_charcode:w \tex_if:D
+\tex_let:D \if_catcode:w \tex_ifcat:D
+\tex_let:D \if_meaning:w \tex_ifx: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_let:D \if_mode_math: \tex_ifmmode:D
+\tex_let:D \if_mode_horizontal: \tex_ifhmode:D
+\tex_let:D \if_mode_vertical: \tex_ifvmode: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_let:D \if_cs_exist:N \tex_ifdefined:D
+\tex_let:D \if_cs_exist:w \tex_ifcsname:D
+\tex_let:D \cs:w \tex_csname: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 \textsf{l3expan} module
+% where they are described.
+% \begin{macrocode}
+\tex_let:D \exp_after:wN \tex_expandafter:D
+\tex_let:D \exp_not:N \tex_noexpand:D
+\tex_let:D \exp_not:n \tex_unexpanded:D
+\tex_let:D \exp:w \tex_romannumeral: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_let:D \token_to_meaning:N \tex_meaning: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_let:D \tl_to_str:n \tex_detokenize:D
+\tex_let:D \token_to_str:N \tex_string: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 \textsf{l3prg} module.
+% \begin{macrocode}
+\tex_let:D \scan_stop: \tex_relax:D
+\tex_let:D \group_begin: \tex_begingroup: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_let:D \if_int_compare:w \tex_ifnum: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_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_def:D \exp_args:Nc #1#2
+ { \exp_after:wN #1 \cs:w #2 \cs_end: }
+\tex_long:D \tex_def: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_def:D \token_to_str:c { \exp_args:Nc \token_to_str:N }
+\tex_long:D \tex_def: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_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 the \textsf{l3alloc} module. The
+% rest are defined in the \textsf{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 \textsf{l3int} module is
+% required but it can't be used until the allocation has been set
+% up properly!
+% \begin{macrocode}
+\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 both in package
+% mode and to bootstrap \pkg{l3alloc}, and is documented in \pkg{l3int}.
+% \begin{macrocode}
+\tex_ifdefined:D \tex_luatexversion:D
+ \tex_chardef:D \c_max_register_int = 65 535 ~
+\tex_else:D
+ \tex_mathchardef:D \c_max_register_int = 32 767 ~
+\tex_fi:D
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Defining functions}
+%
+% We start by providing functions for the typical definition
+% functions. First the local ones.
+%
+% \begin{macro}
+% {
+% \cs_set_nopar:Npn , \cs_set_nopar:Npx ,
+% \cs_set:Npn , \cs_set:Npx ,
+% \cs_set_protected_nopar:Npn , \cs_set_protected_nopar:Npx ,
+% \cs_set_protected:Npn , \cs_set_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_let:D \cs_set_nopar:Npn \tex_def:D
+\tex_let:D \cs_set_nopar:Npx \tex_edef:D
+\tex_protected:D \tex_long:D \tex_def:D \cs_set:Npn
+ { \tex_long:D \tex_def:D }
+\tex_protected:D \tex_long:D \tex_def:D \cs_set:Npx
+ { \tex_long:D \tex_edef:D }
+\tex_protected:D \tex_long:D \tex_def:D \cs_set_protected_nopar:Npn
+ { \tex_protected:D \tex_def:D }
+\tex_protected:D \tex_long:D \tex_def:D \cs_set_protected_nopar:Npx
+ { \tex_protected:D \tex_edef:D }
+\tex_protected:D \tex_long:D \tex_def:D \cs_set_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_def:D }
+\tex_protected:D \tex_long:D \tex_def:D \cs_set_protected:Npx
+ { \tex_protected:D \tex_long:D \tex_edef:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \cs_gset_nopar:Npn , \cs_gset_nopar:Npx ,
+% \cs_gset:Npn , \cs_gset:Npx ,
+% \cs_gset_protected_nopar:Npn , \cs_gset_protected_nopar:Npx ,
+% \cs_gset_protected:Npn , \cs_gset_protected:Npx
+% }
+% Global versions of the above functions.
+% \begin{macrocode}
+\tex_let:D \cs_gset_nopar:Npn \tex_gdef:D
+\tex_let:D \cs_gset_nopar:Npx \tex_xdef:D
+\cs_set_protected:Npn \cs_gset:Npn
+ { \tex_long:D \tex_gdef:D }
+\cs_set_protected:Npn \cs_gset:Npx
+ { \tex_long:D \tex_xdef:D }
+\cs_set_protected:Npn \cs_gset_protected_nopar:Npn
+ { \tex_protected:D \tex_gdef:D }
+\cs_set_protected:Npn \cs_gset_protected_nopar:Npx
+ { \tex_protected:D \tex_xdef:D }
+\cs_set_protected:Npn \cs_gset_protected:Npn
+ { \tex_protected:D \tex_long:D \tex_gdef:D }
+\cs_set_protected:Npn \cs_gset_protected:Npx
+ { \tex_protected:D \tex_long:D \tex_xdef:D }
+% \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_set_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_set:Npn \use:c #1 { \cs:w #1 \cs_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\use:x}
+% Fully expands its argument and passes it to the input stream. Uses
+% the reserved \cs{l_@@_internal_tl} which will be set up in \pkg{l3expan}.
+% \begin{macrocode}
+\cs_set_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}
+% Currently \LuaTeX-only: emulated for older engines.
+% \begin{macrocode}
+\cs_set:Npn \use:e #1 { \tex_expanded:D {#1} }
+\tex_ifdefined:D \tex_expanded:D \tex_else:D
+ \cs_set:Npn \use:e #1 { \exp_args:Ne \use:n {#1} }
+\tex_fi:D
+% \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_set:Npn \use:n #1 {#1}
+\cs_set:Npn \use:nn #1#2 {#1#2}
+\cs_set:Npn \use:nnn #1#2#3 {#1#2#3}
+\cs_set: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_set:Npn \use_i:nn #1#2 {#1}
+\cs_set: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_ii:nnn ,
+% \use_i:nnnn, \use_ii:nnnn, \use_iii:nnnn, \use_iv:nnnn
+% }
+% We also need something for picking up arguments from a longer list.
+% \begin{macrocode}
+\cs_set:Npn \use_i:nnn #1#2#3 {#1}
+\cs_set:Npn \use_ii:nnn #1#2#3 {#2}
+\cs_set:Npn \use_iii:nnn #1#2#3 {#3}
+\cs_set:Npn \use_i_ii:nnn #1#2#3 {#1#2}
+\cs_set:Npn \use_i:nnnn #1#2#3#4 {#1}
+\cs_set:Npn \use_ii:nnnn #1#2#3#4 {#2}
+\cs_set:Npn \use_iii:nnnn #1#2#3#4 {#3}
+\cs_set:Npn \use_iv:nnnn #1#2#3#4 {#4}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\use_ii_i:nn}
+% \begin{macrocode}
+\cs_set: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_set:Npn \use_none_delimit_by_q_nil:w #1 \q_nil { }
+\cs_set:Npn \use_none_delimit_by_q_stop:w #1 \q_stop { }
+\cs_set:Npn \use_none_delimit_by_q_recursion_stop:w #1 \q_recursion_stop { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \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_set:Npn \use_i_delimit_by_q_nil:nw #1#2 \q_nil {#1}
+\cs_set:Npn \use_i_delimit_by_q_stop:nw #1#2 \q_stop {#1}
+\cs_set: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_set:Npn \use_none:n #1 { }
+\cs_set:Npn \use_none:nn #1#2 { }
+\cs_set:Npn \use_none:nnn #1#2#3 { }
+\cs_set:Npn \use_none:nnnn #1#2#3#4 { }
+\cs_set:Npn \use_none:nnnnn #1#2#3#4#5 { }
+\cs_set:Npn \use_none:nnnnnn #1#2#3#4#5#6 { }
+\cs_set:Npn \use_none:nnnnnnn #1#2#3#4#5#6#7 { }
+\cs_set:Npn \use_none:nnnnnnnn #1#2#3#4#5#6#7#8 { }
+\cs_set: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. At present, debugging is disabled in the format and in
+% generic mode, while in \LaTeXe{} mode it is enabled if one of the
+% options \texttt{enable-debug}, \texttt{log-functions} or
+% \texttt{check-declarations} was given.
+% \begin{macrocode}
+\cs_set_protected:Npn \__kernel_if_debug:TF #1#2 {#2}
+%<*package>
+\tex_ifodd:D \l@expl@enable@debug@bool
+ \cs_set_protected:Npn \__kernel_if_debug:TF #1#2 {#1}
+\fi:
+%</package>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\debug_on:n, \debug_off:n, \@@_all_on:, \@@_all_off:}
+%
+% \begin{macrocode}
+\__kernel_if_debug:TF
+ {
+ \cs_set_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: }
+ { \__kernel_msg_error:nnn { kernel } { debug } {##1} }
+ }
+ }
+ \cs_set_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: }
+ { \__kernel_msg_error:nnn { kernel } { debug } {##1} }
+ }
+ }
+ \cs_set_protected:Npn \@@_all_on:
+ {
+ \debug_on:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+ \cs_set_protected:Npn \@@_all_off:
+ {
+ \debug_off:n
+ {
+ check-declarations ,
+ check-expressions ,
+ deprecation ,
+ log-functions ,
+ }
+ }
+ }
+ {
+ \cs_set_protected:Npn \debug_on:n #1
+ {
+ \__kernel_msg_error:nnx { kernel } { enable-debug }
+ { \tl_to_str:n { \debug_on:n {#1} } }
+ }
+ \cs_set_protected:Npn \debug_off:n #1
+ {
+ \__kernel_msg_error:nnx { kernel } { enable-debug }
+ { \tl_to_str:n { \debug_off:n {#1} } }
+ }
+ }
+% \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}
+\__kernel_if_debug:TF
+ {
+ \cs_set_nopar:Npn \l_@@_suspended_tl { }
+ \cs_set_protected:Npn \debug_suspend:
+ {
+ \tl_put_right:Nn \l_@@_suspended_tl { . }
+ \cs_set_eq:NN \@@_suspended:T \use:n
+ }
+ \cs_set_protected:Npn \debug_resume:
+ {
+ \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_set:Npn \@@_suspended:T #1 { }
+ }
+ {
+ \cs_set_protected:Npn \debug_suspend: { }
+ \cs_set_protected:Npn \debug_resume: { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_check-declarations_on:, \@@_check-declarations_off:}
+% \begin{macro}{\__kernel_chk_var_exist:N}
+% \begin{macro}{\__kernel_chk_cs_exist:N, \__kernel_chk_cs_exist:c}
+% \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}
+\__kernel_if_debug:TF
+ {
+ \exp_args:Nc \cs_set_protected:Npn { @@_check-declarations_on: }
+ {
+ \cs_set_protected:Npn \__kernel_chk_var_exist:N ##1
+ {
+ \@@_suspended:T \use_none:nnn
+ \cs_if_exist:NF ##1
+ {
+ \__kernel_msg_error:nnx { kernel } { 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
+ {
+ \__kernel_msg_error:nnx { kernel } { command-not-defined }
+ { \token_to_str:N ##1 }
+ }
+ }
+ \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
+ }
+ }
+ \exp_args:Nc \cs_set_protected:Npn { @@_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_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_set_protected:Npn \__kernel_chk_cs_exist:c
+ { \exp_args:Nc \__kernel_chk_cs_exist:N }
+ \tex_ifodd:D \l@expl@check@declarations@bool
+ \use:c { @@_check-declarations_on: }
+ \else:
+ \use:c { @@_check-declarations_off: }
+ \fi:
+ }
+ { }
+% \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}
+\__kernel_if_debug:TF
+ {
+ \cs_set_protected:Npn \@@_chk_var_scope_aux:NN #1#2
+ { \exp_args:NNf \@@_chk_var_scope_aux:Nn #1 { \cs_to_str:N #2 } }
+ \cs_set_protected:Npn \@@_chk_var_scope_aux:Nn #1#2
+ {
+ \if:w _ \use_i:nn \use_i_delimit_by_q_stop:nw #2 ? ? \q_stop
+ \exp_after:wN \@@_chk_var_scope_aux:NNn
+ \use_i_delimit_by_q_stop:nw #2 ? \q_stop
+ #1 {#2}
+ \else:
+ \exp_args:Nc \@@_chk_var_scope_aux:NNn
+ { @@_chk_/ #2 }
+ #1 {#2}
+ \fi:
+ }
+ \cs_set_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:
+ \__kernel_msg_error:nnxxx { kernel } { local-global }
+ {#1} {#2} { \iow_char:N \\ #3 }
+ \fi:
+ \fi:
+ }
+ }
+ { }
+% \end{macrocode}
+% \end{macro}
+% \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}
+\__kernel_if_debug:TF
+ {
+ \exp_args:Nc \cs_set_protected:Npn { @@_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
+ }
+ }
+ \exp_args:Nc \cs_set_protected:Npn { @@_check-expressions_off: }
+ { \cs_set:Npn \__kernel_chk_expr:nNnN ##1##2##3##4 {##1} }
+ \use:c { @@_check-expressions_off: }
+ \cs_set: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:
+ }
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { expr } {#4} {#1}
+ }
+ #1
+ }
+ }
+ { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_log-functions_on:, \@@_log-functions_off:}
+% \begin{macro}{\__kernel_debug_log:x}
+% These two functions (corresponding to the \pkg{expl3} option
+% \texttt{log-functions}) control whether \cs{__kernel_debug_log:x} writes to the
+% log file or not. Since \cs{iow_log:x} does not yet have its final
+% definition we do not use \cs{cs_set_eq:NN} (not defined yet anyway).
+% Once everything is defined, turn logging on or off depending on what
+% option was given. When debugging is not enabled, simply produce an
+% error.
+% \begin{macrocode}
+\__kernel_if_debug:TF
+ {
+ \exp_args:Nc \cs_set_protected:Npn { @@_log-functions_on: }
+ {
+ \cs_set_protected:Npn \__kernel_debug_log:x
+ { \@@_suspended:T \use_none:nn \iow_log:x }
+ }
+ \exp_args:Nc \cs_set_protected:Npn { @@_log-functions_off: }
+ { \cs_set_protected:Npn \__kernel_debug_log:x { \use_none:n } }
+ \tex_ifodd:D \l@expl@log@functions@bool
+ \use:c { @@_log-functions_on: }
+ \else:
+ \use:c { @@_log-functions_off: }
+ \fi:
+ }
+ { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_deprecation_on:, \@@_deprecation_off:}
+% \begin{macro}{\__kernel_deprecation_code:nn}
+% \begin{variable}{\g_@@_deprecation_on_tl, \g_@@_deprecation_off_tl}
+% Some commands were more recently deprecated and not yet removed;
+% only make these into errors if the user requests it. This relies on
+% two token lists, filled up in \pkg{l3deprecation}.
+% \begin{macrocode}
+\__kernel_if_debug:TF
+ {
+ \cs_set_protected:Npn \@@_deprecation_on:
+ { \g_@@_deprecation_on_tl }
+ \cs_set_protected:Npn \@@_deprecation_off:
+ { \g_@@_deprecation_off_tl }
+ \cs_set_nopar:Npn \g_@@_deprecation_on_tl { }
+ \cs_set_nopar:Npn \g_@@_deprecation_off_tl { }
+ \cs_set_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}
+ }
+ }
+ {
+ \cs_set_protected:Npn \__kernel_deprecation_code:nn #1#2 { }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\__kernel_patch:nnNNpn, \__kernel_patch_conditional:nNNpnn}
+% \begin{macro}
+% {\@@_patch_aux:nnnn, \@@_patch_auxii:nnnn}
+% When debugging is not enabled, \cs{__kernel_patch:nnNNpn} and
+% \cs{__kernel_patch_conditional:nNNpnn} throw the patch away.
+% Otherwise they can be followed by \cs{cs_new:Npn} (or similar), and
+% \cs{prg_new_conditional:Npnn} (or similar), respectively. In each
+% case, grab the name of the function to be defined and its parameters
+% then insert tokens before and/or after the definition.
+% \begin{macrocode}
+\__kernel_if_debug:TF
+ {
+ \cs_set_protected:Npn \__kernel_patch:nnNNpn #1#2#3#4#5#
+ { \@@_patch_aux:nnnn {#1} {#2} { #3 #4 #5 } }
+ \cs_set_protected:Npn \__kernel_patch_conditional:nNNpnn #1#2#3#4#
+ { \@@_patch_auxii:nnnn {#1} { #2 #3 #4 } }
+ \cs_set_protected:Npn \@@_patch_aux:nnnn #1#2#3#4
+ { #3 { #1 #4 #2 } }
+ \cs_set_protected:Npn \@@_patch_auxii:nnnn #1#2#3#4
+ { #2 {#3} { #1 #4 } }
+ }
+ {
+ \cs_set_protected:Npn \__kernel_patch:nnNNpn #1#2 { }
+ \cs_set_protected:Npn \__kernel_patch_conditional:nNNpnn #1 { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\__kernel_patch_args:nNNpn, \__kernel_patch_conditional_args:nNNpnn}
+% \begin{macro}
+% {\__kernel_patch_args:nnnNNpn, \__kernel_patch_conditional_args:nnnNNpnn}
+% \begin{macro}
+% {
+% \@@_tmp:w,
+% \@@_patch_args_aux:nnnNNnn,
+% \@@_patch_args_aux:nnnNNnnn,
+% \@@_patch_args_aux:nnnn
+% }
+% See \cs{__kernel_patch:nnNNpn}. The first argument is something like
+% |{#1}{(#2)}|. Define a temporary macro using the \meta{parameters}
+% and \meta{code} of the definition that follows, then expand that
+% temporary macro in front of the first argument to obtain new
+% \meta{code}. Then perform the definition as if that new \meta{code}
+% was directly typed in the file.
+% \begin{macrocode}
+\cs_set_protected:Npn \__kernel_patch_args:nNNpn
+ { \__kernel_patch_args:nnnNNpn { } { } }
+\cs_set_protected:Npn \__kernel_patch_conditional_args:nNNpnn
+ { \__kernel_patch_conditional_args:nnnNNpnn { } { } }
+\__kernel_if_debug:TF
+ {
+ \cs_set_protected:Npn \__kernel_patch_args:nnnNNpn #1#2#3#4#5#6#
+ { \@@_patch_args_aux:nnnNNnn {#1} {#2} {#3} #4 #5 {#6} }
+ \cs_set_protected:Npn \__kernel_patch_conditional_args:nnnNNpnn
+ #1#2#3#4#5#6#
+ { \@@_patch_args_aux:nnnNNnnn {#1} {#2} {#3} #4 #5 {#6} }
+ \cs_set_protected:Npn \@@_patch_args_aux:nnnNNnn #1#2#3#4#5#6#7
+ {
+ \cs_set:Npn \@@_tmp:w #6 {#7}
+ \exp_after:wN \@@_patch_args_aux:nnnn \exp_after:wN
+ { \@@_tmp:w #3 } { #4 #5 #6 } {#1} {#2}
+ }
+ \cs_set_protected:Npn \@@_patch_args_aux:nnnNNnnn #1#2#3#4#5#6#7#8
+ {
+ \cs_set:Npn \@@_tmp:w #6 {#8}
+ \exp_after:wN \@@_patch_args_aux:nnnn \exp_after:wN
+ { \@@_tmp:w #3 } { #4 #5 #6 {#7} } {#1} {#2}
+ }
+ \cs_set_protected:Npn \@@_patch_args_aux:nnnn #1#2#3#4
+ { #2 { #3 #1 #4 } }
+ }
+ {
+ \cs_set_protected:Npn \__kernel_patch_args:nnnNNpn #1#2#3 { }
+ \cs_set_protected:Npn \__kernel_patch_conditional_args:nnnNNpnn
+ #1#2#3 { }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \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_set:Npn \prg_return_true:
+ { \exp_after:wN \use_i:nn \exp:w }
+\cs_set: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}
+% {
+% \prg_set_conditional:Npnn ,
+% \prg_new_conditional:Npnn ,
+% \prg_set_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_set_protected:Npn \prg_set_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_set:Npn e }
+\cs_set_protected:Npn \prg_new_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_new:Npn e }
+\cs_set_protected:Npn \prg_set_protected_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_set_protected:Npn p }
+\cs_set_protected:Npn \prg_new_protected_conditional:Npnn
+ { \@@_generate_conditional_parm:NNNpnn \cs_new_protected:Npn p }
+\cs_set_protected:Npn \@@_generate_conditional_parm:NNNpnn #1#2#3#4#
+ {
+ \use:x
+ {
+ \@@_generate_conditional:nnNNNnnn
+ \cs_split_function:N #3
+ }
+ #1 #2 {#4}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \prg_set_conditional:Nnn ,
+% \prg_new_conditional:Nnn ,
+% \prg_set_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_set_protected:Npn \prg_set_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_set:Npn e }
+\cs_set_protected:Npn \prg_new_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_new:Npn e }
+\cs_set_protected:Npn \prg_set_protected_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_set_protected:Npn p }
+\cs_set_protected:Npn \prg_new_protected_conditional:Nnn
+ { \@@_generate_conditional_count:NNNnn \cs_new_protected:Npn p }
+\cs_set_protected:Npn \@@_generate_conditional_count:NNNnn #1#2#3
+ {
+ \use:x
+ {
+ \@@_generate_conditional_count:nnNNNnn
+ \cs_split_function:N #3
+ }
+ #1 #2
+ }
+\cs_set_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} }
+ {
+ \__kernel_msg_error:nnxx { 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_set_protected:Npn \@@_generate_conditional:nnNNNnnn #1#2#3#4#5#6#7#8
+ {
+ \if_meaning:w \c_false_bool #3
+ \__kernel_msg_error:nnx { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \exp_after:wN \use_none:nn
+ \fi:
+ \use:x
+ {
+ \exp_not:N \@@_generate_conditional:NNnnnnNw
+ \exp_not:n { #4 #5 {#1} {#2} {#6} }
+ \@@_generate_conditional_test:w
+ #8 \q_mark
+ \@@_generate_conditional_fast:nw
+ \prg_return_true: \else: \prg_return_false: \fi: \q_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_set:Npn \@@_generate_conditional_test:w
+ #1 \prg_return_true: \else: \prg_return_false: \fi: \q_mark #2
+ { #2 {#1} }
+\cs_set: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_set_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}
+ {
+ \__kernel_msg_error:nnxx
+ { kernel } { conditional-form-unknown }
+ {#8} { \token_to_str:c { #3 : #4 } }
+ }
+ \use_none:nnnnnnnn
+ \q_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}
+% 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
+% could replace \cs{exp_after:wN} \cs{use_ii:nnn} and similar by a
+% single macro similar to \cs{@@_p_true:w}. The drawback is that if
+% the |T| or |F| arguments are actually missing, the recovery from
+% the runaway argument would not insert \cs{fi:} back, messing up
+% nesting of conditionals.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_generate_p_form:wNNnnnnN
+ #1 \q_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 }
+ }
+ {
+ \__kernel_msg_error:nnx { kernel } { protected-predicate }
+ { \token_to_str:c { #4 _p: #5 } }
+ }
+ }
+\cs_set_protected:Npn \@@_generate_T_form:wNNnnnnN
+ #1 \q_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 \exp_after:wN \use_ii:nn \fi: \use_none:n }
+ }
+\cs_set_protected:Npn \@@_generate_F_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 F } #6 }
+ { { #7 \exp_end: { } } }
+ { #7 \exp_after:wN \use_none:nn \fi: \use:n }
+ }
+\cs_set_protected:Npn \@@_generate_TF_form:wNNnnnnN
+ #1 \q_stop #2#3#4#5#6#7#8
+ {
+ #8
+ { \exp_args:Nc #2 { #4 : #5 TF } #6 }
+ { { #7 \exp_end: } }
+ { #7 \exp_after:wN \use_ii:nnn \fi: \use_ii:nn }
+ }
+\cs_set:Npn \@@_p_true:w \fi: \c_false_bool { \fi: \c_true_bool }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\prg_set_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_set_protected:Npn \prg_set_eq_conditional:NNn
+ { \@@_set_eq_conditional:NNNn \cs_set_eq:cc }
+\cs_set_protected:Npn \prg_new_eq_conditional:NNn
+ { \@@_set_eq_conditional:NNNn \cs_new_eq:cc }
+\cs_set_protected:Npn \@@_set_eq_conditional:NNNn #1#2#3#4
+ {
+ \use:x
+ {
+ \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_set_protected:Npn \@@_set_eq_conditional:nnNnnNNw #1#2#3#4#5#6
+ {
+ \if_meaning:w \c_false_bool #3
+ \__kernel_msg_error:nnx { 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
+ \__kernel_msg_error:nnx { 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_set_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}
+ {
+ \__kernel_msg_error:nnxx
+ { kernel } { conditional-form-unknown }
+ {#6} { \token_to_str:c { #1 : #2 } }
+ }
+ \use_none:nnnnnn
+ \q_stop
+ #5 {#1} {#2} {#3} {#4}
+ \@@_set_eq_conditional_loop:nnnnNw {#1} {#2} {#3} {#4} #5
+ }
+\__kernel_patch:nnNNpn
+ { \__kernel_chk_cs_exist:c { #5 _p : #6 } } { }
+\cs_set:Npn \@@_set_eq_conditional_p_form:wNnnnn #1 \q_stop #2#3#4#5#6
+ { #2 { #3 _p : #4 } { #5 _p : #6 } }
+\__kernel_patch:nnNNpn
+ { \__kernel_chk_cs_exist:c { #5 : #6 TF } } { }
+\cs_set:Npn \@@_set_eq_conditional_TF_form:wNnnnn #1 \q_stop #2#3#4#5#6
+ { #2 { #3 : #4 TF } { #5 : #6 TF } }
+\__kernel_patch:nnNNpn
+ { \__kernel_chk_cs_exist:c { #5 : #6 T } } { }
+\cs_set:Npn \@@_set_eq_conditional_T_form:wNnnnn #1 \q_stop #2#3#4#5#6
+ { #2 { #3 : #4 T } { #5 : #6 T } }
+\__kernel_patch:nnNNpn
+ { \__kernel_chk_cs_exist:c { #5 : #6 F } } { }
+\cs_set:Npn \@@_set_eq_conditional_F_form:wNnnnn #1 \q_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_chardef:D \c_true_bool = 1 ~
+\tex_chardef:D \c_false_bool = 0 ~
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Dissecting a control sequence}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \begin{function}{\@@_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}[EXP]{\@@_get_function_name:N}
+% \begin{syntax}
+% \cs{@@_get_function_name: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{name} is then left in the input stream without the escape
+% character present made up of tokens with category code $12$
+% (other).
+% \end{function}
+%
+% \begin{function}[EXP]{\@@_get_function_signature:N}
+% \begin{syntax}
+% \cs{@@_get_function_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{signature} is then left in the input stream made up of
+% tokens with category code $12$ (other).
+% \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_set: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_set:Npn \@@_to_str:N #1 { \c_zero_int }
+\cs_set: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.
+%
+% We cannot use |:| directly as it has the wrong category code so
+% an |x|-type expansion is used to force the conversion.
+%
+% 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{q_mark},
+% then \cs{c_true_bool} as |#3|, and |#4| cleans up until \cs{q_stop}.
+% Otherwise, the |#1| contains the function name and \cs{q_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{q_mark} from the function name if
+% present (that is, if the original function had no colon).
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_set:Npn \cs_split_function:N ##1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \@@_split_function_auxi:w
+ \cs_to_str:N ##1 \q_mark \c_true_bool
+ #1 \q_mark \c_false_bool \q_stop
+ }
+ \cs_set:Npn \@@_split_function_auxi:w
+ ##1 #1 ##2 \q_mark ##3##4 \q_stop
+ { \@@_split_function_auxii:w ##1 \q_mark \q_stop {##2} ##3 }
+ \cs_set:Npn \@@_split_function_auxii:w ##1 \q_mark ##2 \q_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}
+% 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:}.
+% \begin{macrocode}
+\prg_set_conditional:Npnn \cs_if_exist:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \scan_stop:
+ \prg_return_false:
+ \else:
+ \if_cs_exist:N #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \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}
+\prg_set_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop:
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+ \prg_return_false:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\cs_if_free:N, \cs_if_free:c}
+% The logical reversal of the above.
+% \begin{macrocode}
+\prg_set_conditional:Npnn \cs_if_free:N #1 { p , T , F , TF }
+ {
+ \if_meaning:w #1 \scan_stop:
+ \prg_return_true:
+ \else:
+ \if_cs_exist:N #1
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \fi:
+ }
+\prg_set_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w #1 \cs_end:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ { \prg_return_true: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,noTF,added=2011-10-10]
+% {\cs_if_exist_use:N, \cs_if_exist_use:c}
+% The \cs[index=cs_if_exist_use:N]{cs_if_exist_use:\ldots{}}
+% functions cannot be implemented
+% as conditionals because the true branch must leave both the control
+% sequence itself and the true code in the input stream.
+% For the \texttt{c} variants, we are careful not to put the control
+% sequence in the hash table if it does not exist.
+% In \LuaTeX{} we could use the \tn{lastnamedcs} primitive.
+% \begin{macrocode}
+\cs_set:Npn \cs_if_exist_use:NTF #1#2
+ { \cs_if_exist:NTF #1 { #1 #2 } }
+\cs_set:Npn \cs_if_exist_use:NF #1
+ { \cs_if_exist:NTF #1 { #1 } }
+\cs_set:Npn \cs_if_exist_use:NT #1 #2
+ { \cs_if_exist:NTF #1 { #1 #2 } { } }
+\cs_set:Npn \cs_if_exist_use:N #1
+ { \cs_if_exist:NTF #1 { #1 } { } }
+\cs_set:Npn \cs_if_exist_use:cTF #1#2
+ { \cs_if_exist:cTF {#1} { \use:c {#1} #2 } }
+\cs_set:Npn \cs_if_exist_use:cF #1
+ { \cs_if_exist:cTF {#1} { \use:c {#1} } }
+\cs_set:Npn \cs_if_exist_use:cT #1#2
+ { \cs_if_exist:cTF {#1} { \use:c {#1} #2 } { } }
+\cs_set:Npn \cs_if_exist_use:c #1
+ { \cs_if_exist:cTF {#1} { \use:c {#1} } { } }
+% \end{macrocode}
+% \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}
+% {\__kernel_msg_error:nnxx, \__kernel_msg_error:nnx, \__kernel_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_set_protected:Npn \__kernel_msg_error:nnxx #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_set_protected:Npn \__kernel_msg_error:nnx #1#2#3
+ { \__kernel_msg_error:nnxx {#1} {#2} {#3} { } }
+\cs_set_protected:Npn \__kernel_msg_error:nn #1#2
+ { \__kernel_msg_error:nnxx {#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_set: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:x, \iow_term:x}
+% 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{l3io}.
+% \begin{macrocode}
+\cs_set_protected:Npn \iow_log:x
+ { \tex_immediate:D \tex_write:D -1 }
+\cs_set_protected:Npn \iow_term:x
+ { \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}
+\__kernel_patch:nnNNpn { }
+ {
+ \__kernel_debug_log:x
+ { Defining~\token_to_str:N #1~ \msg_line_context: }
+ }
+\cs_set_protected:Npn \__kernel_chk_if_free_cs:N #1
+ {
+ \cs_if_free:NF #1
+ {
+ \__kernel_msg_error:nnxx { kernel } { command-already-defined }
+ { \token_to_str:N #1 } { \token_to_meaning:N #1 }
+ }
+ }
+\cs_set_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:Npx ,
+% \cs_new:Npn , \cs_new:Npx ,
+% \cs_new_protected_nopar:Npn , \cs_new_protected_nopar:Npx ,
+% \cs_new_protected:Npn , \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_set_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:Npx \cs_gset_nopar:Npx
+\@@_tmp:w \cs_new:Npn \cs_gset:Npn
+\@@_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:Npx \cs_gset_protected_nopar:Npx
+\@@_tmp:w \cs_new_protected:Npn \cs_gset_protected:Npn
+\@@_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:cpx ,
+% \cs_gset_nopar:cpn , \cs_gset_nopar:cpx ,
+% \cs_new_nopar:cpn , \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:cpx \cs_set_nopar:Npx
+\@@_tmp:w \cs_gset_nopar:cpn \cs_gset_nopar:Npn
+\@@_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:cpx \cs_new_nopar:Npx
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set:Npn]
+% {
+% \cs_set:cpn , \cs_set:cpx ,
+% \cs_gset:cpn , \cs_gset:cpx ,
+% \cs_new:cpn , \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:cpx \cs_set:Npx
+\@@_tmp:w \cs_gset:cpn \cs_gset:Npn
+\@@_tmp:w \cs_gset:cpx \cs_gset:Npx
+\@@_tmp:w \cs_new:cpn \cs_new:Npn
+\@@_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:cpx ,
+% \cs_gset_protected_nopar:cpn , \cs_gset_protected_nopar:cpx ,
+% \cs_new_protected_nopar:cpn , \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:cpx \cs_set_protected_nopar:Npx
+\@@_tmp:w \cs_gset_protected_nopar:cpn \cs_gset_protected_nopar:Npn
+\@@_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: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:cpx ,
+% \cs_gset_protected:cpn , \cs_gset_protected:cpx ,
+% \cs_new_protected:cpn , \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:cpx \cs_set_protected:Npx
+\@@_tmp:w \cs_gset_protected:cpn \cs_gset_protected:Npn
+\@@_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: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.
+% \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:
+ \exp_after:wN \use:n
+ \else:
+ \exp_after:wN \use_none:n
+ \fi:
+ { \cs_gset_eq:cN {#1} \tex_undefined:D }
+ }
+% \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_set_protected:Npn \__kernel_cs_parm_from_arg_count:nnF #1#2
+ {
+ \exp_args:Nx \@@_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_set_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}
+ {
+ \__kernel_msg_error:nnxx { 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:Nx ,
+% \cs_set_nopar:Nn , \cs_set_nopar:Nx ,
+% \cs_set_protected:Nn , \cs_set_protected:Nx ,
+% \cs_set_protected_nopar:Nn , \cs_set_protected_nopar:Nx ,
+% \cs_gset:Nn , \cs_gset:Nx ,
+% \cs_gset_nopar:Nn , \cs_gset_nopar:Nx ,
+% \cs_gset_protected:Nn , \cs_gset_protected:Nx ,
+% \cs_gset_protected_nopar:Nn , \cs_gset_protected_nopar:Nx ,
+% \cs_new:Nn , \cs_new:Nx ,
+% \cs_new_nopar:Nn , \cs_new_nopar:Nx ,
+% \cs_new_protected:Nn , \cs_new_protected:Nx ,
+% \cs_new_protected_nopar:Nn , \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:x
+ {
+ \@@_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
+ {
+ \str_if_eq:eeF { }
+ { \tl_map_function:nN {#2} \@@_generate_from_signature:n }
+ {
+ \__kernel_msg_error:nnx { kernel } { non-base-function }
+ { \token_to_str:N #5 }
+ }
+ \cs_generate_from_arg_count:NNnn
+ #5 #4 { \tl_count:n {#2} } {#6}
+ }
+ {
+ \__kernel_msg_error:nnx { 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 } { Nx } { Npx }
+\@@_tmp:w { set_nopar } { Nn } { Npn }
+\@@_tmp:w { set_nopar } { Nx } { Npx }
+\@@_tmp:w { set_protected } { Nn } { Npn }
+\@@_tmp:w { set_protected } { Nx } { Npx }
+\@@_tmp:w { set_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { set_protected_nopar } { Nx } { Npx }
+\@@_tmp:w { gset } { Nn } { Npn }
+\@@_tmp:w { gset } { Nx } { Npx }
+\@@_tmp:w { gset_nopar } { Nn } { Npn }
+\@@_tmp:w { gset_nopar } { Nx } { Npx }
+\@@_tmp:w { gset_protected } { Nn } { Npn }
+\@@_tmp:w { gset_protected } { Nx } { Npx }
+\@@_tmp:w { gset_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { gset_protected_nopar } { Nx } { Npx }
+\@@_tmp:w { new } { Nn } { Npn }
+\@@_tmp:w { new } { Nx } { Npx }
+\@@_tmp:w { new_nopar } { Nn } { Npn }
+\@@_tmp:w { new_nopar } { Nx } { Npx }
+\@@_tmp:w { new_protected } { Nn } { Npn }
+\@@_tmp:w { new_protected } { Nx } { Npx }
+\@@_tmp:w { new_protected_nopar } { Nn } { Npn }
+\@@_tmp:w { new_protected_nopar } { Nx } { Npx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[documented-as=\cs_set:Nn]
+% {
+% \cs_set:cn , \cs_set:cx ,
+% \cs_set_nopar:cn , \cs_set_nopar:cx ,
+% \cs_set_protected:cn , \cs_set_protected:cx ,
+% \cs_set_protected_nopar:cn , \cs_set_protected_nopar:cx ,
+% \cs_gset:cn , \cs_gset:cx ,
+% \cs_gset_nopar:cn , \cs_gset_nopar:cx ,
+% \cs_gset_protected:cn , \cs_gset_protected:cx ,
+% \cs_gset_protected_nopar:cn , \cs_gset_protected_nopar:cx ,
+% \cs_new:cn , \cs_new:cx ,
+% \cs_new_nopar:cn , \cs_new_nopar:cx ,
+% \cs_new_protected:cn , \cs_new_protected:cx ,
+% \cs_new_protected_nopar:cn , \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 } { x }
+\@@_tmp:w { set_nopar } { n }
+\@@_tmp:w { set_nopar } { x }
+\@@_tmp:w { set_protected } { n }
+\@@_tmp:w { set_protected } { x }
+\@@_tmp:w { set_protected_nopar } { n }
+\@@_tmp:w { set_protected_nopar } { x }
+\@@_tmp:w { gset } { n }
+\@@_tmp:w { gset } { x }
+\@@_tmp:w { gset_nopar } { n }
+\@@_tmp:w { gset_nopar } { x }
+\@@_tmp:w { gset_protected } { n }
+\@@_tmp:w { gset_protected } { x }
+\@@_tmp:w { gset_protected_nopar } { n }
+\@@_tmp:w { gset_protected_nopar } { x }
+\@@_tmp:w { new } { n }
+\@@_tmp:w { new } { x }
+\@@_tmp:w { new_nopar } { n }
+\@@_tmp:w { new_nopar } { x }
+\@@_tmp:w { new_protected } { n }
+\@@_tmp:w { new_protected } { x }
+\@@_tmp:w { new_protected_nopar } { n }
+\@@_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}
+ {
+ \__kernel_msg_error:nnx { 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{x}-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:NNx
+ \group_end:
+ #1 { \token_to_str:N #2 = \cs_meaning:N #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Decomposing a macro definition}
+%
+% \begin{macro}{\cs_prefix_spec:N}
+% \begin{macro}{\cs_argument_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 argument 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:x
+ {
+ \exp_not:n { \cs_new:Npn \@@_prefix_arg_replacement:wN #1 }
+ \tl_to_str:n { macro : } \exp_not:n { #2 -> #3 \q_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 \q_stop \use_i:nnn
+ }
+ { \scan_stop: }
+ }
+\cs_new:Npn \cs_argument_spec:N #1
+ {
+ \token_if_macro:NTF #1
+ {
+ \exp_after:wN \@@_prefix_arg_replacement:wN
+ \token_to_meaning:N #1 \q_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 \q_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}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3bootstrap.dtx b/macros/latex/contrib/l3kernel/l3bootstrap.dtx
new file mode 100644
index 0000000000..35ab971e15
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3bootstrap.dtx
@@ -0,0 +1,575 @@
+% \iffalse meta-comment
+%
+%% File: l3bootstrap.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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} package\\ Bootstrap code^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Using the \LaTeX3 modules}
+%
+% The modules documented in \file{source3} are designed to be used on top of
+% \LaTeXe{} and are loaded all as one with the usual |\usepackage{expl3}| or
+% |\RequirePackage{expl3}| instructions. These modules will also form the
+% basis of the \LaTeX3 format, but work in this area is incomplete and not
+% included in this documentation at present.
+%
+% 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
+% r\'{e}gime in which spaces 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 r\'{e}gime.
+% \end{function}
+%
+% \begin{function}[updated = 2017-03-19]
+% {\ProvidesExplPackage, \ProvidesExplClass, \ProvidesExplFile}
+% \begin{syntax}
+% |\RequirePackage{expl3}| \\
+% \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}. If the
+% \meta{version} is given then it will be prefixed with \texttt{v} in
+% the package identifier line.
+% \end{function}
+%
+% \begin{function}[updated = 2012-06-04]{\GetIdInfo}
+% \begin{syntax}
+% |\RequirePackage{l3bootstrap}|
+% \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}
+%<*initex|package>
+%<@@=kernel>
+% \end{macrocode}
+%
+% \subsection{Format-specific code}
+%
+% The very first thing to do is to bootstrap the \IniTeX{} system so
+% that everything else will actually work. \TeX{} does not start with
+% some pretty basic character codes set up.
+% \begin{macrocode}
+%<*initex>
+\catcode `\{ = 1 %
+\catcode `\} = 2 %
+\catcode `\# = 6 %
+\catcode `\^ = 7 %
+%</initex>
+% \end{macrocode}
+%
+% Tab characters should not show up in the code, but to be on the
+% safe side.
+% \begin{macrocode}
+%<*initex>
+\catcode `\^^I = 10 %
+%</initex>
+% \end{macrocode}
+%
+% For \LuaTeX{}, the extra primitives need to be enabled. This is not needed
+% in package mode: common formats have the primitives enabled.
+% \begin{macrocode}
+%<*initex>
+\begingroup\expandafter\expandafter\expandafter\endgroup
+\expandafter\ifx\csname directlua\endcsname\relax
+\else
+ \directlua{tex.enableprimitives("", tex.extraprimitives())}%
+\fi
+%</initex>
+% \end{macrocode}
+%
+% Depending on the versions available, the \LaTeX{} format may not have
+% the raw |\Umath| primitive names available. We fix that globally:
+% it should cause no issues. Older \LuaTeX{} versions do not have
+% a pre-built table of the primitive names here so sort one out
+% ourselves. These end up globally-defined but at that is true with
+% a newer format anyway and as they all start |\U| this should be
+% reasonably safe.
+% \begin{macrocode}
+%<*package>
+\begingroup
+ \expandafter\ifx\csname directlua\endcsname\relax
+ \else
+ \directlua{%
+ local i
+ local t = { }
+ for _,i in pairs(tex.extraprimitives("luatex")) do
+ if string.match(i,"^U") then
+ if not string.match(i,"^Uchar$") then %$
+ table.insert(t,i)
+ end
+ end
+ end
+ tex.enableprimitives("", t)
+ }%
+ \fi
+\endgroup
+%</package>
+% \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<70 %
+ \else
+% \end{macrocode}
+% In package mode a category code table is needed: either use a pre-loaded
+% allocator or provide one using the \LaTeXe{}-based generic code. In format
+% mode the table used here can be hard-coded into the \Lua{}.
+% \begin{macrocode}
+%<*package>
+ \begingroup\expandafter\expandafter\expandafter\endgroup
+ \expandafter\ifx\csname newcatcodetable\endcsname\relax
+ \input{ltluatex}%
+ \fi
+ \newcatcodetable\ucharcat@table
+ \directlua{
+ l3kernel = l3kernel or { }
+ local charcat_table = \number\ucharcat@table\space
+ l3kernel.charcat_table = charcat_table
+ }%
+%</package>
+ \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{} and functionality equivalent to
+% \tn{pdfstrcmp}, and also driver and Unicode character support. This is
+% available in a reasonably-wide range of engines.
+% \begin{macrocode}
+\begingroup
+ \def\next{\endgroup}%
+ \def\ShortText{Required primitives not found}%
+ \def\LongText%
+ {%
+ LaTeX3 requires the e-TeX primitives and additional functionality as
+ described in the README file.
+ \LineBreak
+ These are available in the engines\LineBreak
+ - pdfTeX v1.40\LineBreak
+ - XeTeX v0.99992\LineBreak
+ - LuaTeX v0.76\LineBreak
+ - e-(u)pTeX mid-2012\LineBreak
+ or later.\LineBreak
+ \LineBreak
+ }%
+ \ifnum0%
+ \expandafter\ifx\csname pdfstrcmp\endcsname\relax
+ \else
+ \expandafter\ifx\csname pdftexversion\endcsname\relax
+ \expandafter\ifx\csname Ucharcat\endcsname\relax
+ \expandafter\ifx\csname kanjiskip\endcsname\relax
+ \else
+ 1%
+ \fi
+ \else
+ 1%
+ \fi
+ \else
+ \ifnum\pdftexversion<140 \else 1\fi
+ \fi
+ \fi
+ \expandafter\ifx\csname directlua\endcsname\relax
+ \else
+ \ifnum\luatexversion<76 \else 1\fi
+ \fi
+ =0 %
+ \newlinechar`\^^J %
+%<*initex>
+ \def\LineBreak{^^J}%
+ \edef\next
+ {%
+ \errhelp
+ {%
+ \LongText
+ For pdfTeX and XeTeX the '-etex' command-line switch is also
+ needed.\LineBreak
+ \LineBreak
+ Format building will abort!\LineBreak
+ }%
+ \errmessage{\ShortText}%
+ \endgroup
+ \noexpand\end
+ }%
+%</initex>
+%<*package>
+ \def\LineBreak{\noexpand\MessageBreak}%
+ \expandafter\ifx\csname PackageError\endcsname\relax
+ \def\LineBreak{^^J}%
+ \def\PackageError#1#2#3%
+ {%
+ \errhelp{#3}%
+ \errmessage{#1 Error: #2}%
+ }%
+ \fi
+ \edef\next
+ {%
+ \noexpand\PackageError{expl3}{\ShortText}
+ {\LongText Loading of expl3 will abort!}%
+ \endgroup
+ \noexpand\endinput
+ }%
+%</package>
+ \fi
+\next
+% \end{macrocode}
+%
+% \subsection{Extending allocators}
+%
+% In format mode, allocating registers is handled by \pkg{l3alloc}. However, in
+% package mode it's much safer to rely on more general code. For example,
+% the ability to extend \TeX{}'s allocation routine to allow for \eTeX{} has
+% been around since 1997 in the \pkg{etex} package.
+%
+% Loading this support is delayed until here as we are now sure that the
+% \eTeX{} extensions and \tn{pdfstrcmp} or equivalent are available. Thus
+% there is no danger of an \enquote{uncontrolled} error if the engine
+% requirements are not met.
+%
+% For \LaTeXe{} we need to make sure that the extended pool is being used:
+% \pkg{expl3} uses a lot of registers. For formats from 2015 onward there is
+% nothing to do as this is automatic. For older formats, the \pkg{etex}
+% package needs to be loaded to do the job. In that case, some inserts are
+% reserved also as these have to be from the standard pool. Note that
+% \tn{reserveinserts} is \tn{outer} and so is accessed here by csname. In
+% earlier versions, loading \pkg{etex} was done directly and so
+% \tn{reserveinserts} appeared in the code: this then required a \tn{relax}
+% after \tn{RequirePackage} to prevent an error with \enquote{unsafe}
+% definitions as seen for example with \pkg{capoptions}. The optional loading
+% here is done using a group and \tn{ifx} test as we are not quite in the
+% position to have a single name for \tn{pdfstrcmp} just yet.
+% \begin{macrocode}
+%<*package>
+\begingroup
+ \def\@tempa{LaTeX2e}%
+ \def\next{}%
+ \ifx\fmtname\@tempa
+ \expandafter\ifx\csname extrafloats\endcsname\relax
+ \def\next
+ {%
+ \RequirePackage{etex}%
+ \csname reserveinserts\endcsname{32}%
+ }%
+ \fi
+ \fi
+\expandafter\endgroup
+\next
+%</package>
+% \end{macrocode}
+%
+% \subsection{Character data}
+%
+% \TeX{} needs various pieces of data to be set about characters, in particular
+% which ones to treat as letters and which \tn{lccode} values apply as these
+% affect hyphenation. It makes most sense to set this and related information
+% up in one place. Whilst for \LuaTeX{} hyphenation patterns can be read
+% anywhere, other engines have to build them into the format and so we
+% \emph{must} do this set up before reading the patterns. For the Unicode
+% engines, there are shared loaders available to obtain the relevant
+% information directly from the Unicode Consortium data files. These need
+% standard (Ini)\TeX{} category codes and primitive availability and must
+% therefore loaded \emph{very} early. This has a knock-on effect on the
+% $8$-bit set up: it makes sense to do the definitions for those here as
+% well so it is all in one place.
+%
+% For \XeTeX{} and \LuaTeX{}, which are natively Unicode engines, simply
+% load the Unicode data.
+% \begin{macrocode}
+%<*initex>
+\ifdefined\Umathcode
+ \input load-unicode-data %
+ \input load-unicode-math-classes %
+\else
+% \end{macrocode}
+% For the $8$-bit engines a font encoding scheme must be chosen. At present,
+% this is the EC (|T1|) scheme, with the assumption that languages for which
+% this is not appropriate will be used with one of the Unicode engines.
+% \begin{macrocode}
+ \begingroup
+% \end{macrocode}
+% Lower case chars: map to themselves when lower casing and down by |"20| when
+% upper casing. (The characters |a|--|z| are set up correctly by \IniTeX{}.)
+% \begin{macrocode}
+ \def\temp{%
+ \ifnum\count0>\count2 %
+ \else
+ \global\lccode\count0 = \count0 %
+ \global\uccode\count0 = \numexpr\count0 - "20\relax
+ \advance\count0 by 1 %
+ \expandafter\temp
+ \fi
+ }
+ \count0 = "A0 %
+ \count2 = "BC %
+ \temp
+ \count0 = "E0 %
+ \count2 = "FF %
+ \temp
+% \end{macrocode}
+% Upper case chars: map up by |"20| when lower casing, to themselves when upper
+% casing and require an \tn{sfcode} of $999$. (The characters |A|--|Z| are set
+% up correctly by \IniTeX{}.)
+% \begin{macrocode}
+ \def\temp{%
+ \ifnum\count0>\count2 %
+ \else
+ \global\lccode\count0 = \numexpr\count0 + "20\relax
+ \global\uccode\count0 = \count0 %
+ \global\sfcode\count0 = 999 %
+ \advance\count0 by 1 %
+ \expandafter\temp
+ \fi
+ }
+ \count0 = "80 %
+ \count2 = "9C %
+ \temp
+ \count0 = "C0 %
+ \count2 = "DF %
+ \temp
+% \end{macrocode}
+% A few special cases where things are not as one might expect using the above
+% pattern: dotless-I, dotless-J, dotted-I and d-bar.
+% \begin{macrocode}
+ \global\lccode`\^^Y = `\^^Y %
+ \global\uccode`\^^Y = `\I %
+ \global\lccode`\^^Z = `\^^Z %
+ \global\uccode`\^^Y = `\J %
+ \global\lccode"9D = `\i %
+ \global\uccode"9D = "9D %
+ \global\lccode"9E = "9E %
+ \global\uccode"9E = "D0 %
+% \end{macrocode}
+% Allow hyphenation at a zero-width glyph (used to break up ligatures or
+% to place accents between characters).
+% \begin{macrocode}
+ \global\lccode23 = 23 %
+ \endgroup
+\fi
+%</initex>
+% \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. For format mode, there is no need to save
+% category codes so that step is skipped.
+% \begin{macrocode}
+\protected\def\ExplSyntaxOff{}%
+%<*package>
+\protected\edef\ExplSyntaxOff
+ {%
+ \protected\def\ExplSyntaxOff{}%
+ \catcode 9 = \the\catcode 9\relax
+ \catcode 32 = \the\catcode 32\relax
+ \catcode 34 = \the\catcode 34\relax
+ \catcode 38 = \the\catcode 38\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
+ }%
+%</package>
+% \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 38 = 4\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 experimental code syntax: this is on at present.
+% \begin{macrocode}
+\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:Npx \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 { 38 } { \char_value_catcode:n { 38 } }
+ \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_alignment:n { 38 } % ampersand
+ \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3box.dtx b/macros/latex/contrib/l3kernel/l3box.dtx
new file mode 100644
index 0000000000..8067a6ca6a
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3box.dtx
@@ -0,0 +1,2279 @@
+% \iffalse meta-comment
+%
+%% File: l3box.dtx
+%
+% Copyright (C) 2005-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Boxes^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% 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_|.
+%
+% \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{dimexpr} \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{dimexpr} 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{dimexpr} \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{dimexpr} 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{dimension expression}.
+% \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{dimension expression}.
+% \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{dimension expression}.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{wd}.
+% \end{texnote}
+% \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{dimension expression}
+% \end{syntax}
+% Set the depth (below the baseline) of the \meta{box} to the value of
+% the \Arg{dimension expression}.
+% \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{dimension expression}
+% \end{syntax}
+% Set the height (above the baseline) of the \meta{box} to the value of
+% the \Arg{dimension expression}.
+% \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{dimension expression}
+% \end{syntax}
+% Set the width of the \meta{box} to the value of the
+% \Arg{dimension expression}.
+% \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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Display the contents of \meta{box} in the terminal, showing the first
+% \meta{intexpr_1} items of the box, and descending into \meta{intexpr_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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Writes the contents of \meta{box} to the log, showing the first
+% \meta{intexpr_1} items of the box, and descending into \meta{intexpr_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{dimexpr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a horizontal box of width
+% \meta{dimexpr} 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{dimexpr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} to the width given by the \meta{dimexpr}
+% and then stores the result inside the \meta{box}.
+% \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{dimexpr} \meta{contents} \cs{hbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} to the width given by the \meta{dimexpr}
+% 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{dimexpr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} into a vertical box of height
+% \meta{dimexpr} 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{dimexpr} \Arg{contents}
+% \end{syntax}
+% Typesets the \meta{contents} to the height given by the
+% \meta{dimexpr} 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{dimexpr} \meta{contents} \cs{vbox_set_end:}
+% \end{syntax}
+% Typesets the \meta{contents} to the height given by the \meta{dimexpr}
+% 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{dimexpr}
+% \end{syntax}
+% Sets \meta{box_1} to contain material to the height given by the
+% \meta{dimexpr} 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 \tn{box} primitive.
+% \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} (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
+% integer expressions). 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{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}
+%<*initex|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
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1}
+ \@@_dim_eval:w { } \@@_dim_eval:n
+ }
+ }
+\cs_new:Npn \@@_dim_eval:n #1
+ { \@@_dim_eval:w #1 \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+% \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}
+%<*package>
+\cs_new_protected:Npn \box_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newbox \cs_end: #1
+ }
+%</package>
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \box_set_eq:NN #1#2
+ { \tex_setbox:D #1 \tex_copy:D #2 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \box_set_eq_drop:NN #1#2
+ { \tex_setbox:D #1 \tex_box:D #2 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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_set_ht:Nn, \box_set_ht:cn}
+% \begin{macro}{\box_set_dp:Nn, \box_set_dp:cn}
+% \begin{macro}{\box_set_wd:Nn, \box_set_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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \box_set_to_last:N #1
+ { \tex_setbox:D #1 \tex_lastbox:D }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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_set:Nn \tex_interactionmode:D { 0 }
+ \@@_show:NNff 0 #2 { \int_eval:n {#3} } { \int_eval:n {#4} }
+ \int_set: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:
+ }
+ {
+ \__kernel_msg_error:nnx { 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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \hbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ { \color_group_begin: #2 \color_group_end: }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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: }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \hbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_hbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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:
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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_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_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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \vbox_set:Nn #1#2
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ { \color_group_begin: #2 \par \color_group_end: }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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: }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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: }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \vbox_set:Nw #1
+ {
+ \tex_setbox:D #1 \tex_vbox:D
+ \c_group_begin_token
+ \color_group_begin:
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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:
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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
+ {
+ \tex_kern:D -\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
+ {
+ \hbox_set:Nn #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_autosize_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 { ( #2 ) / \box_wd:N #1 }
+ \fp_set:Nn \l_@@_scale_y_fp { ( #3 ) / ( #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 }
+ {
+ \tex_kern:D \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
+ {
+ \tex_kern:D \c_zero_dim
+ \box_use_drop:N \l_@@_internal_box
+ \tex_hss:D
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3candidates.dtx b/macros/latex/contrib/l3kernel/l3candidates.dtx
new file mode 100644
index 0000000000..a92db3ebed
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3candidates.dtx
@@ -0,0 +1,4480 @@
+% \iffalse meta-comment
+%
+%% File: l3candidates.dtx
+%
+% Copyright (C) 2012-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3candidates} package\\ Experimental additions to
+% \pkg{l3kernel}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Important notice}
+%
+% This module provides a space in which functions can be added to
+% \pkg{l3kernel} (\pkg{expl3}) while still being experimental.
+% \begin{quote}
+% \bfseries
+% As such, the
+% functions here may not remain in their current form, or indeed at all,
+% in \pkg{l3kernel} in the future.
+% \end{quote}
+% In contrast to the material in
+% \pkg{l3experimental}, the functions here are all \emph{small} additions to
+% the kernel. We encourage programmers to test them out and report back on
+% the \texttt{LaTeX-L} mailing list.
+%
+% \medskip
+%
+% Thus, if you intend to use any of these functions from the candidate module in a public package
+% offered to others for productive use (e.g., being placed on CTAN) please consider the following points carefully:
+% \begin{itemize}
+% \item Be prepared that your public packages might require updating when such functions
+% are being finalized.
+% \item Consider informing us that you use a particular function in your public package, e.g., by
+% discussing this on the \texttt{LaTeX-L}
+% mailing list. This way it becomes easier to coordinate any updates necessary without issues
+% for the users of your package.
+% \item Discussing and understanding use cases for a particular addition or concept also helps to
+% ensure that we provide the right interfaces in the final version so please give us feedback
+% if you consider a certain candidate function useful (or not).
+% \end{itemize}
+% We only add functions in this space if we consider them being serious candidates for a final inclusion
+% into the kernel. However, real use sometimes leads to better ideas, so functions from this module are
+% \textbf{not necessarily stable} and we may have to adjust them!
+%
+% \section{Additions to \pkg{l3basics}}
+%
+% \begin{function}[added = 2017-07-16, updated = 2017-08-02]{\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 soon-to-be-deprecated commands produce errors;
+% \item \texttt{log-functions} that logs function definitions;
+% \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. These functions can only be used in \LaTeXe{} package mode
+% loaded with \texttt{enable-debug} or another option implying it.
+% \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 or warnings. 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{l3coffins}.
+% \end{function}
+%
+% \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{Additions to \pkg{l3box}}
+%
+% \subsection{Viewing part of a box}
+%
+% \begin{function}[updated = 2019-01-23]
+% {\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c}
+% \begin{syntax}
+% \cs{box_clip: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.
+%
+% \textbf{These functions require the \LaTeX3 native drivers: they do
+% not work with the \LaTeXe{} \pkg{graphics} drivers!}
+%
+% \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{dimension expressions}.
+% Material outside of the bounding box is still displayed in the output
+% unless \cs{box_clip: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.
+% 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{dimension expressions}. Material outside of the bounding box is
+% still displayed in the output unless \cs{box_clip: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.
+% \end{function}
+%
+% \section{Additions to \pkg{l3expan}}
+%
+% \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{Additions to \pkg{l3fp}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{logb}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |logb(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Determines the exponent of the \meta{fpexpr}, 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}[EXP, added = 2018-11-03]{\fp_sign:n}
+% \begin{syntax}
+% \cs{fp_sign:n} \Arg{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{fpexpr} 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}[pTF, added = 2019-08-25]{\fp_if_nan:n}
+% \begin{syntax}
+% \cs{fp_if_nan:n} \Arg{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{fpexpr} 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{Additions to \pkg{l3fparray}}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_new:Nn}
+% \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}[EXP, added = 2018-05-05]{\fparray_count:N}
+% \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}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gset:Nnn}
+% \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}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gzero:N}
+% \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}
+%
+% \begin{function}[EXP, added = 2018-05-05]
+% {\fparray_item:Nn, \fparray_item_to_tl:Nn}
+% \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}, an
+% error occurs.
+% \end{function}
+%
+% \section{Additions to \pkg{l3file}}
+%
+% \begin{function}[added = 2019-05-13, TF]{\file_compare_timestamp:nNn}
+% \begin{syntax}
+% \cs{file_compare_timestamp:nNn} \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.
+% \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 = 2018-12-29]{\iow_allow_break:}
+% \begin{syntax}
+% \cs{iow_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.
+% In other words this is a zero-width breaking space.
+% \end{function}
+%
+% \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}
+%
+% \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_shell_get:nnN(TF)}.
+% \end{function}
+%
+% \section{Additions to \pkg{l3flag}}
+%
+% \begin{function}[EXP, added = 2018-04-02]{\flag_raise_if_clear:n}
+% \begin{syntax}
+% \cs{flag_raise_if_clear:n} \Arg{flag name}
+% \end{syntax}
+% Ensures the \meta{flag} is raised by making its height at least~$1$,
+% locally.
+% \end{function}
+%
+% \section{Additions to \pkg{l3int}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\int_sign:n}
+% \begin{syntax}
+% \cs{int_sign:n} \Arg{intexpr}
+% \end{syntax}
+% Evaluates the \meta{integer expression} then leaves $1$ or $0$ or
+% $-1$ in the input stream according to the sign of the result.
+% \end{function}
+%
+% \section{Additions to \pkg{l3intarray}}
+%
+% \begin{function}[added = 2018-05-05]{\intarray_gset_rand:Nnn, \intarray_gset_rand:Nn}
+% \begin{syntax}
+% \cs{intarray_gset_rand:Nnn} \meta{intarray~var} \Arg{minimum} \Arg{maximum}
+% \cs{intarray_gset_rand:Nn} \meta{intarray~var} \Arg{maximum}
+% \end{syntax}
+% Evaluates the integer expressions \meta{minimum} and \meta{maximum}
+% then sets each entry (independently) of the \meta{integer array
+% variable} to a pseudo-random number between the two (with bounds
+% included). If the absolute value of either bound is bigger than
+% $2^{30}-1$, an error occurs. Entries are generated in the same way
+% as repeated calls to \cs{int_rand:nn} or \cs{int_rand:n}
+% respectively, in particular for the second function the
+% \meta{minimum} is $1$.
+% Assignments are always global.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \subsection{Working with contents of integer arrays}
+%
+% \begin{function}[added = 2018-05-04, rEXP]{\intarray_const_from_clist:Nn}
+% \begin{syntax}
+% \cs{intarray_const_from_clist:Nn} \meta{intarray~var} \meta{intexpr 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, rEXP]{\intarray_to_clist:N}
+% \begin{syntax}
+% \cs{intarray_to_clist:N} \meta{intarray~var}
+% \end{syntax}
+% Converts the \meta{intarray} to integer denotations separated by
+% commas. All tokens have category code other. If the
+% \meta{intarray} has no entry the result is empty; otherwise the
+% result has one fewer comma than the number of items.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-04]{\intarray_show:N, \intarray_log:N}
+% \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{Additions to \pkg{l3msg}}
+%
+% 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{::error} 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}
+%
+% \begin{function}[added = 2017-12-04]{\msg_show_eval:Nn, \msg_log_eval:Nn}
+% \begin{syntax}
+% \cs{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}[added = 2017-12-04]
+% {
+% \msg_show:nnnnnn ,
+% \msg_show:nnnnn ,
+% \msg_show:nnnn ,
+% \msg_show:nnn ,
+% \msg_show:nn ,
+% \msg_show:nnxxxx ,
+% \msg_show:nnxxx ,
+% \msg_show:nnxx ,
+% \msg_show:nnx
+% }
+% \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:nnxxxx} to show or log
+% a list of items or key--value pairs. The one-argument functions are
+% used for sequences, clist or token lists and the others for property
+% lists. These functions turn their arguments to strings.
+% \end{function}
+%
+% \section{Additions to \pkg{l3prg}}
+%
+% \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}[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}
+%
+% \section{Additions to \pkg{l3prop}}
+%
+% \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}[EXP, added = 2016-12-06]
+% {\prop_rand_key_value:N, \prop_rand_key_value:c}
+% \begin{syntax}
+% \cs{prop_rand_key_value:N} \meta{prop~var}
+% \end{syntax}
+% Selects a pseudo-random key--value pair from the \meta{property list}
+% and returns \Arg{key} and \Arg{value}. If the \meta{property list} 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{value}
+% does not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2019-08-25]
+% {
+% \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{prop~var}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Sets \meta{prop~var} to contain key--value pairs given in the second
+% argument. If duplicate keys appear only one of the values is kept.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2019-08-25]
+% {\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
+% \begin{syntax}
+% \cs{prop_const_from_keyval:Nn} \meta{prop~var}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Creates a new constant \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.
+% If duplicate keys appear only one of the values is kept.
+% \end{function}
+%
+% \section{Additions to \pkg{l3seq}}
+%
+% \begin{function}[rEXP]
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{syntax}
+% \cs{seq_mapthread_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}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{syntax}
+% \cs{seq_set_filter:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline boolexpr}
+% \end{syntax}
+% Evaluates the \meta{inline boolexpr} for every \meta{item} stored
+% within the \meta{sequence_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{sequence_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 = 2011-12-22]
+% {\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{syntax}
+% \cs{seq_set_map:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{sequence_2}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The sequence resulting from \texttt{x}-expanding
+% \meta{inline function} applied to each \meta{item}
+% is assigned to \meta{sequence_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}[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 = 2018-04-06]
+% {\seq_set_from_function:NnN, \seq_gset_from_function:NnN}
+% \begin{syntax}
+% \cs{seq_set_from_function:NnN} \meta{seq~var} \Arg{loop~code} \meta{function}
+% \end{syntax}
+% Sets the \meta{seq~var} equal to a sequence whose items are obtained
+% by \texttt{x}-expanding \meta{loop~code} \meta{function}. This
+% expansion must result in successive calls to the \meta{function}
+% with no nonexpandable tokens in between. More precisely the
+% \meta{function} is replaced by a wrapper function that inserts the
+% appropriate separators between items in the sequence. The
+% \meta{loop~code} must be expandable; it can be for example
+% \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN}
+% \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value}
+% \Arg{step} \Arg{final value}.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-06]
+% {\seq_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn}
+% \begin{syntax}
+% \cs{seq_set_from_inline_x:Nnn} \meta{seq~var} \Arg{loop~code} \Arg{inline~code}
+% \end{syntax}
+% Sets the \meta{seq~var} equal to a sequence whose items are obtained
+% by \texttt{x}-expanding \meta{loop~code} applied to a
+% \meta{function} derived from the \meta{inline~code}. A
+% \meta{function} is defined, that takes one argument,
+% \texttt{x}-expands the \meta{inline~code} with that argument
+% as~|#1|, then adds appropriate separators to turn the result into an
+% item of the sequence. The \texttt{x}-expansion of \meta{loop~code}
+% \meta{function} must result in successive calls to the
+% \meta{function} with no nonexpandable tokens in between. The
+% \meta{loop~code} must be expandable; it can be for example
+% \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN}
+% \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value}
+% \Arg{step} \Arg{final value}, but not the analogous \enquote{inline}
+% mappings.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-29]{\seq_shuffle:N, \seq_gshuffle:N}
+% \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}
+%
+% \begin{function}[added = 2018-05-03]{\seq_indexed_map_function:NN}
+% \begin{syntax}
+% \cs{seq_indexed_map_function:NN} \meta{seq~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every entry in the \meta{sequence
+% variable}. 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_indexed_map_inline:Nn}
+% \begin{syntax}
+% \cs{seq_indexed_map_inline:Nn} \meta{seq~var} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every entry in the \meta{sequence
+% variable}. 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}
+%
+% \section{Additions to \pkg{l3skip}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\dim_sign:n}
+% \begin{syntax}
+% \cs{dim_sign:n} \Arg{dimexpr}
+% \end{syntax}
+% Evaluates the \meta{dimexpr} then leaves $1$ or $0$ or $-1$ in the
+% input stream according to the sign of the result.
+% \end{function}
+%
+% \section{Additions to \pkg{l3sys}}
+%
+% \begin{function}[noTF, added = 2019-01-16, updated = 2019-02-16]
+% {\sys_shell_get:nnN}
+% \begin{syntax}
+% \cs{sys_shell_get:nnN} \Arg{shell~command} \Arg{setup} \meta{tl~var}
+% \cs{sys_shell_get:nnNTF} \Arg{shell~command} \Arg{setup} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Defines \meta{tl} 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_shell_get:nnNTF} conditional
+% returns \texttt{true} if the shell is available and no quote is
+% detected, and \texttt{false} otherwise.
+% \end{function}
+%
+% \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 = 2017-05-27, EXP, pTF]{\sys_if_rand_exist:}
+% \begin{syntax}
+% \cs{sys_if_rand_exist_p:}
+% \cs{sys_if_rand_exist:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the engine has a pseudo-random number generator. Currently
+% this is the case in \pdfTeX{}, \LuaTeX{}, \pTeX{} and \upTeX{}.
+% \end{function}
+%
+% \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{intexpr}
+% \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}
+%
+% \begin{function}[added = 2018-07-27, EXP, pTF]
+% {
+% \sys_if_platform_unix:,
+% \sys_if_platform_windows:
+% }
+% \begin{syntax}
+% \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}
+%
+% \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:}.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]{\sys_shell_now:n, \sys_shell_now:x}
+% \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:x}
+% \begin{syntax}
+% \cs{sys_shell_shipout:n} \Arg{tokens}
+% \end{syntax}
+% Execute \meta{tokens} through shell escape at shipout.
+% \end{function}
+%
+% \section{Additions to \pkg{l3tl}}
+%
+% \begin{function}[EXP, added = 2014-06-30, updated = 2016-01-12]
+% {
+% \tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n,
+% \tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn
+% }
+% \begin{syntax}
+% \cs{tl_upper_case:n} \Arg{tokens}
+% \cs{tl_upper_case:nn} \Arg{language} \Arg{tokens}
+% \end{syntax}
+% These functions are intended to be applied to input which may be
+% regarded broadly as \enquote{text}. They traverse the \meta{tokens} and
+% change the case of characters as discussed below. The character code of
+% the characters replaced may be arbitrary: the replacement characters
+% have standard document-level category codes ($11$ for letters, $12$ for
+% letter-like characters which can also be case-changed). Begin-group and
+% end-group characters in the \meta{tokens} are normalized and become |{|
+% and |}|, respectively.
+%
+% Importantly, notice that these functions are intended for working with
+% user text for typesetting. For case changing programmatic data see the
+% \pkg{l3str} module and discussion there of \cs{str_lower_case:n},
+% \cs{str_upper_case:n} and \cs{str_fold_case:n}.
+% \end{function}
+%
+% The functions perform expansion on the input in most cases. In particular,
+% input in the form of token lists or expandable functions is expanded
+% \emph{unless} it falls within one of the special handling classes described
+% below. This expansion approach means that in general the result of case
+% changing matches the \enquote{natural} outcome expected from a
+% \enquote{functional} approach to case modification. For example
+% \begin{verbatim}
+% \tl_set:Nn \l_tmpa_tl { hello }
+% \tl_upper_case:n { \l_tmpa_tl \c_space_tl world }
+% \end{verbatim}
+% produces
+% \begin{verbatim}
+% HELLO WORLD
+% \end{verbatim}
+% The expansion approach taken means that in package mode any \LaTeXe{}
+% \enquote{robust} commands which may appear in the input should be converted
+% to engine-protected versions using for example the \tn{robustify} command
+% from the \pkg{etoolbox} package.
+%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% Case changing does not take place within math mode material so for example
+% \begin{verbatim}
+% \tl_upper_case:n { Some~text~$y = mx + c$~with~{Braces} }
+% \end{verbatim}
+% becomes
+% \begin{verbatim}
+% SOME TEXT $y = mx + c$ WITH {BRACES}
+% \end{verbatim}
+% Material inside math mode is left entirely unchanged: in particular, no
+% expansion is undertaken.
+%
+% Detection of math mode is controlled by the list of tokens in
+% \cs{l_tl_case_change_math_tl}, which should be in open--close pairs. In
+% package mode the standard settings is
+% \begin{verbatim}
+% $ $ \( \)
+% \end{verbatim}
+%
+% Note that while expansion occurs when searching the text it does not
+% apply to math mode material (which should be unaffected by case changing).
+% As such, whilst the opening token for math mode may be \enquote{hidden}
+% inside a command/macro, the closing one cannot be as this is being
+% searched for in math mode. Typically, in the types of \enquote{text}
+% the case changing functions are intended to apply to this should not be
+% an issue.
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% Case changing can be prevented by using any command on the list
+% \cs{l_tl_case_change_exclude_tl}. Each entry should be a function
+% to be followed by one argument: the latter will be preserved as-is
+% with no expansion. Thus for example following
+% \begin{verbatim}
+% \tl_put_right:Nn \l_tl_case_change_exclude_tl { \NoChangeCase }
+% \end{verbatim}
+% the input
+% \begin{verbatim}
+% \tl_upper_case:n
+% { Some~text~$y = mx + c$~with~\NoChangeCase {Protection} }
+% \end{verbatim}
+% will result in
+% \begin{verbatim}
+% SOME TEXT $y = mx + c$ WITH \NoChangeCase {Protection}
+% \end{verbatim}
+% Notice that the case changing mapping preserves the inclusion of
+% the escape functions: it is left to other code to provide suitable
+% definitions (typically equivalent to \cs{use:n}). In particular, the
+% result of case changing is returned protected by \cs{exp_not:n}.
+%
+% When used with \LaTeXe{} the commands |\cite|, |\ensuremath|, |\label|
+% and |\ref| are automatically included in the list for exclusion from
+% case changing.
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_accents_tl}
+% This list specifies accent commands which should be left unexpanded
+% in the output. This allows for example
+% \begin{verbatim}
+% \tl_upper_case:n { \" { a } }
+% \end{verbatim}
+% to yield
+% \begin{verbatim}
+% \" { A }
+% \end{verbatim}
+% irrespective of the expandability of |\"|.
+%
+% The standard contents of this variable is |\"|, |\'|, |\.|, |\^|, |\`|,
+% |\~|, |\c|, |\H|, |\k|, |\r|, |\t|, |\u| and |\v|.
+% \end{variable}
+%
+% \enquote{Mixed} case conversion may be regarded informally as converting the
+% first character of the \meta{tokens} to upper case and the rest to lower
+% case. However, the process is more complex than this as there are some
+% situations where a single lower case character maps to a special form, for
+% example \texttt{ij} in Dutch which becomes \texttt{IJ}. As such,
+% \cs[index=tl_mixed_case:n]{tl_mixed_case:n(n)}
+% implement a more sophisticated mapping which accounts
+% for this and for modifying accents on the first letter. Spaces at the start
+% of the \meta{tokens} are ignored when finding the first \enquote{letter} for
+% conversion.
+% \begin{verbatim}
+% \tl_mixed_case:n { hello~WORLD } % => "Hello world"
+% \tl_mixed_case:n { ~hello~WORLD } % => " Hello world"
+% \tl_mixed_case:n { {hello}~WORLD } % => "{Hello} world"
+% \end{verbatim}
+% When finding the first \enquote{letter} for this process, any content in
+% math mode or covered by \cs{l_tl_case_change_exclude_tl} is ignored.
+%
+% (Note that the Unicode Consortium describe this as \enquote{title case}, but
+% that in English title case applies on a word-by-word basis. The
+% \enquote{mixed} case implemented here is a lower level concept needed for
+% both \enquote{title} and \enquote{sentence} casing of text.)
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% The list of characters to ignore when searching for the first
+% \enquote{letter} in mixed-casing is determined by
+% \cs{l_tl_mixed_change_ignore_tl}. This has the standard setting
+% \begin{verbatim}
+% ( [ { ` -
+% \end{verbatim}
+% where comparisons are made on a character basis.
+% \end{variable}
+%
+% As is generally true for \pkg{expl3}, these functions are designed to
+% work with Unicode input only. As such, UTF-8 input is assumed for
+% \emph{all} engines. When used with \XeTeX{} or \LuaTeX{} a full range of
+% Unicode transformations are enabled. Specifically, 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}. In the case of $8$-bit engines, mappings
+% are provided for characters which can be represented in output typeset
+% using the |T1| font encoding. Thus for example |ä| can be case-changed
+% using \pdfTeX{}. For \pTeX{} only the ASCII range is covered as the
+% engine treats input outside of this range as east Asian.
+%
+% Context-sensitive mappings are enabled: language-dependent cases are
+% discussed below. Context detection expands input but treats any
+% unexpandable control sequences as \enquote{failures} to match a context.
+%
+% Language-sensitive conversions are enabled using the \meta{language}
+% argument, and follow Unicode Consortium guidelines. Currently, the
+% languages recognised for special handling are as follows.
+% \begin{itemize}
+% \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 lower
+% casing I-dot and introduced when upper casing i-dotless.
+% \item German (\texttt{de-alt}).
+% An alternative mapping for German in which the lower case
+% \emph{Eszett} maps to a \emph{gro\ss{}es Eszett}.
+% \item Lithuanian (\texttt{lt}).
+% The lower case letters i and j should retain a dot above when the
+% accents grave, acute or tilde are present. This is implemented for
+% lower casing of the relevant upper case letters both when input as
+% single Unicode codepoints and when using combining accents. The
+% combining dot is removed when upper casing in these cases. Note that
+% \emph{only} the accents used in Lithuanian are covered: the behaviour
+% of other accents are not modified.
+% \item Dutch (\texttt{nl}).
+% Capitalisation of \texttt{ij} at the beginning of mixed cased
+% input produces \texttt{IJ} rather than \texttt{Ij}. The output
+% retains two separate letters, thus this transformation \emph{is}
+% available using \pdfTeX{}.
+% \end{itemize}
+%
+% Creating additional context-sensitive mappings requires knowledge
+% of the underlying mapping implementation used here. The team are happy
+% to add these to the kernel where they are well-documented
+% (\emph{e.g.}~in Unicode Consortium or relevant government publications).
+%
+% \begin{function}[EXP, added = 2017-07-15]
+% {
+% \tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn,
+% \tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn
+% }
+% \begin{syntax}
+% \cs{tl_range_braced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index}
+% \cs{tl_range_braced:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
+% \cs{tl_range_unbraced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index}
+% \cs{tl_range_unbraced: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, using the same indexing as
+% \cs{tl_range:nnn}. Spaces are ignored. Regardless of whether items
+% appear with or without braces in the \meta{token list}, the
+% \cs{tl_range_braced:nnn} function wraps each item in braces, while
+% \cs{tl_range_unbraced:nnn} does not (overall it removes an outer set
+% of braces). For instance,
+% \begin{verbatim}
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 2 } { 5 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -4 } { -1 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -2 } { -1 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 0 } { -1 } }
+% \end{verbatim}
+% prints \verb*|{b}{c}{d}{e{}}|, \verb*|{c}{d}{e{}}{f}|, \verb*|{e{}}{f}|, and an empty
+% line to the terminal, while
+% \begin{verbatim}
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 2 } { 5 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -4 } { -1 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -2 } { -1 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 0 } { -1 } }
+% \end{verbatim}
+% prints \verb*|bcde{}|, \verb*|cde{}f|, \verb*|e{}f|, and an empty
+% line to the terminal. Because braces are removed, the result of
+% \cs{tl_range_unbraced:nnn} may have a different number of items as
+% for \cs{tl_range:nnn} or \cs{tl_range_braced:nnn}. In cases where
+% preserving spaces is important, consider the slower function
+% \cs{tl_range:nnn}.
+% \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{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \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, which allow accumulating
+% large numbers of tokens piece by piece much more efficiently than
+% standard \pkg{l3tl} functions. Until \cs{tl_build_end: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_clear:N, \tl_build_gclear:N}
+% \begin{syntax}
+% \cs{tl_build_clear: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. The |clear| and
+% |gclear| 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:Nx,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Nx,
+% \tl_build_put_right:Nn, \tl_build_put_right:Nx,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Nx,
+% }
+% \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_get:NN}
+% \begin{syntax}
+% \cs{tl_build_get:N} \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}
+%
+% \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}. 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.
+% \end{function}
+%
+% \section{Additions to \pkg{l3token}}
+%
+% \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{function}[added = 2018-04-06, updated = 2019-05-03, EXP]
+% {
+% \char_lower_case:N, \char_upper_case:N,
+% \char_mixed_case:N, \char_fold_case:N,
+% \char_str_lower_case:N, \char_str_upper_case:N,
+% \char_str_mixed_case:N, \char_str_fold_case:N,
+% }
+% \begin{syntax}
+% \cs{char_lower_case:N} \meta{char}
+% \end{syntax}
+% Converts the \meta{char} to the equivalent case-changed character
+% as detailed by the function name (see \cs{str_fold_case:n}
+% and \cs{tl_mixed_case:n} for details of these terms). The case mapping
+% is carried out with no context-dependence (\emph{cf.}~\cs{tl_upper_case:n},
+% \emph{etc.}) The \texttt{str} versions always generate \enquote{other}
+% (category code $12$) characters, whilst the standard versions generate
+% characters with the currently-active category code (\emph{i.e.}~as if
+% the character had been read directly here).
+% \end{function}
+%
+% \begin{function}[added = 2018-06-01, EXP]{\char_codepoint_to_bytes:n}
+% \begin{syntax}
+% \cs{char_codepoint_to_bytes:n} \Arg{codepoint}
+% \end{syntax}
+% Converts the (Unicode) \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 grouos 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}[added = 2018-09-23]
+% {
+% \peek_catcode_collect_inline:Nn,
+% \peek_charcode_collect_inline:Nn,
+% \peek_meaning_collect_inline:Nn
+% }
+% \begin{syntax}
+% \cs{peek_catcode_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \cs{peek_charcode_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \cs{peek_meaning_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \end{syntax}
+% Collects and removes tokens from the input stream until finding a
+% token that does not match the \meta{test token} (as defined by the
+% test \cs{token_if_eq_catcode:NNTF} or \cs{token_if_eq_charcode:NNTF}
+% or \cs{token_if_eq_meaning:NNTF}). The collected tokens are passed
+% to the \meta{inline code} as~|#1|. When begin-group or end-group
+% tokens (usually |{| or~|}|) are collected they are replaced by
+% implicit \cs{c_group_begin_token} and \cs{c_group_end_token}, and
+% when spaces (including \cs{c_space_token}) are collected they are
+% replaced by explicit spaces.
+%
+% For example the following code prints ``Hello'' to the terminal and
+% leave ``, world!'' in the input stream.
+% \begin{verbatim}
+% \peek_catcode_collect_inline:Nn A { \iow_term:n {#1} } Hello,~world!
+% \end{verbatim}
+% Another example is that the following code tests if the next token is |*|, ignoring intervening spaces, but putting them back using |#1| if there is no~|*|.
+% \begin{verbatim}
+% \peek_meaning_collect_inline:Nn \c_space_token
+% { \peek_charcode:NTF * { star } { no~star #1 } }
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[added = 2018-10-01]{\peek_remove_spaces:n}
+% \begin{syntax}
+% \cs{peek_remove_spaces:n} \Arg{code}
+% \end{syntax}
+% Removes explicit and implicit space tokens (category code~$10$ and
+% character code~$32$) from the input stream, then inserts \meta{code}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3candidates} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \subsection{Additions to \pkg{l3basics}}
+%
+% \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 either the \tn{quitvmode} primitive or the equivalent
+% protected macro. 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}
+%
+% \subsection{Additions to \pkg{l3box}}
+%
+% \begin{macrocode}
+%<@@=box>
+% \end{macrocode}
+%
+% \subsubsection{Viewing part of a box}
+%
+% \begin{macro}{\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c}
+% A wrapper around the driver-dependent code.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_clip:N #1
+ { \hbox_set:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_clip:N { c }
+\cs_new_protected:Npn \box_gclip:N #1
+ { \hbox_gset:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_gclip: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
+ {
+ \tex_kern:D - \@@_dim_eval:n {#2}
+ \box_use:N #1
+ \tex_kern:D - \@@_dim_eval: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
+ {
+ \tex_kern:D - \@@_dim_eval:n {#2}
+ \box_use:N #1
+ \tex_kern:D \@@_dim_eval: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}
+%
+% \subsection{Additions to \pkg{l3fp-convert}}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \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}
+%
+% \subsection{Additions to \pkg{l3flag}}
+%
+% \begin{macrocode}
+%<@@=flag>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\flag_raise_if_clear:n}
+% It might be faster to just call the \enquote{trap} function in all
+% cases but conceptually the function name suggests we should only run
+% it if the flag is zero in case the \enquote{trap} made customizable
+% in the future.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \@@_chk_exist:n {#1} } { }
+\cs_new:Npn \flag_raise_if_clear:n #1
+ {
+ \if_cs_exist:w flag~#1~0 \cs_end:
+ \else:
+ \cs:w flag~#1 \cs_end: 0 ;
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3int}}
+%
+% \begin{macrocode}
+%<@@=int>
+% \end{macrocode}
+%
+% \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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_sign:n } }
+\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}
+%
+% \subsection{Additions to \pkg{l3msg}}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% \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
+% }
+% \begin{macro}{\@@_expandable_error_module:nn}
+% Pass to an auxiliary the message to display and the module name
+% \begin{macrocode}
+\cs_new:Npn \msg_expandable_error:nnnnnn #1#2#3#4#5#6
+ {
+ \exp_args:Ne \@@_expandable_error_module: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} }
+ }
+ {#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 }
+\cs_new:Npn \@@_expandable_error_module:nn #1#2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_none_delimit_by_q_stop:w
+ \use:n { \::error ! ~ #2 : ~ #1 } \q_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\msg_show_eval:Nn, \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 \msg_show_eval:Nn #1#2
+ { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_show:n }
+\cs_new_protected:Npn \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}
+%
+% \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.
+% \begin{macrocode}
+\cs_new:Npx \msg_show_item:n #1
+ { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n { {#1} } }
+\cs_new:Npx \msg_show_item_unbraced:n #1
+ { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n {#1} }
+\cs_new:Npx \msg_show_item:nn #1#2
+ {
+ \iow_newline: > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#1} }
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#2} }
+ }
+\cs_new:Npx \msg_show_item_unbraced:nn #1#2
+ {
+ \iow_newline: > \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}
+%
+% \subsection{Additions to \pkg{l3prg}}
+%
+% \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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\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}[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{Additions to \pkg{l3prop}}
+%
+% \begin{macrocode}
+%<@@=prop>
+% \end{macrocode}
+%
+% \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{macro}{\@@_map_tokens:nwwn}
+% The mapping is very similar to \cs{prop_map_function:NN}. The
+% \cs{use_i:nn} removes the leading \cs{s_@@}. The odd construction
+% |\use:n {#1}| allows |#1| to contain any token without interfering
+% with \cs{prop_map_break:}. The loop stops when the argument
+% delimited by \cs{@@_pair:wn} is \cs{prg_break:} instead of being
+% empty.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nno
+ \use_i:nn { \@@_map_tokens:nwwn {#2} } #1
+ \prg_break: \@@_pair:wn \s_@@ { } \prg_break_point:
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_tokens:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4
+ {
+ #2
+ \use:n {#1} {#3} {#4}
+ \@@_map_tokens:nwwn {#1}
+ }
+\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\prop_rand_key_value:N, \prop_rand_key_value:c}
+% \begin{macro}[EXP]{\@@_rand_item:w}
+% Contrarily to |clist|, |seq| and |tl|, there is no function to get
+% an item of a |prop| given an integer between $1$ and the number of
+% items, so we write the appropriate code. There is no bounds
+% checking because \cs{int_rand:nn} is always within bounds. The
+% initial \cs{int_value:w} is stopped by the first \cs{s_@@} in~|#1|.
+% \begin{macrocode}
+\cs_new:Npn \prop_rand_key_value:N #1
+ {
+ \prop_if_empty:NF #1
+ {
+ \exp_after:wN \@@_rand_item:w
+ \int_value:w \int_rand:nn { 1 } { \prop_count:N #1 }
+ #1 \q_stop
+ }
+ }
+\cs_generate_variant:Nn \prop_rand_key_value:N { c }
+\cs_new:Npn \@@_rand_item:w #1 \s_@@ \@@_pair:wn #2 \s_@@ #3
+ {
+ \int_compare:nNnF {#1} > 1
+ { \use_i_delimit_by_q_stop:nw { \exp_not:n { {#2} {#3} } } }
+ \exp_after:wN \@@_rand_item:w
+ \int_value:w \int_eval:n { #1 - 1 } \s_@@
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3seq}}
+%
+% \begin{macrocode}
+%<@@=seq>
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{macro}
+% {
+% \@@_mapthread_function:wNN, \@@_mapthread_function:wNw,
+% \@@_mapthread_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_mapthread_function:NNN #1#2#3
+ { \exp_after:wN \@@_mapthread_function:wNN #2 \q_stop #1 #3 }
+\cs_new:Npn \@@_mapthread_function:wNN \s_@@ #1 \q_stop #2#3
+ {
+ \exp_after:wN \@@_mapthread_function:wNw #2 \q_stop #3
+ #1 { ? \prg_break: } { }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_mapthread_function:wNw \s_@@ #1 \q_stop #2
+ {
+ \@@_mapthread_function:Nnnwnn #2
+ #1 { ? \prg_break: } { }
+ \q_stop
+ }
+\cs_new:Npn \@@_mapthread_function:Nnnwnn #1#2#3#4 \q_stop #5#6
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 {#3} {#6}
+ \@@_mapthread_function:Nnnwnn #1 #4 \q_stop
+ }
+\cs_generate_variant:Nn \seq_mapthread_function:NNN { Nc , c , cc }
+% \end{macrocode}
+% \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{x}-expanding assignment.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_filter:NNn
+ { \@@_set_filter:NNNn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_filter:NNn
+ { \@@_set_filter:NNNn \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_set_map:NNn, \seq_gset_map:NNn}
+% \begin{macro}{\@@_set_map: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:NNn
+ { \@@_set_map:NNNn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map:NNn
+ { \@@_set_map:NNNn \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_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn}
+% \begin{macro}{\@@_set_from_inline_x:NNnn}
+% Set \cs{@@_item:n} then map it using the loop code.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_inline_x:Nnn
+ { \@@_set_from_inline_x:NNnn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_from_inline_x:Nnn
+ { \@@_set_from_inline_x:NNnn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_from_inline_x:NNnn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} }
+ #1 #2 { \s_@@ #3 \@@_item:n }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_from_function:NnN, \seq_gset_from_function:NnN}
+% Reuse \cs{seq_set_from_inline_x:Nnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_function:NnN #1#2#3
+ { \seq_set_from_inline_x:Nnn #1 {#2} { #3 {##1} } }
+\cs_new_protected:Npn \seq_gset_from_function:NnN #1#2#3
+ { \seq_gset_from_inline_x:Nnn #1 {#2} { #3 {##1} } }
+% \end{macrocode}
+% \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:Nx #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_shuffle:N, \seq_gshuffle:N, \@@_shuffle:NN, \@@_shuffle_item:n}
+% \begin{variable}{\g_@@_internal_seq, \l_@@_internal_a_int, \l_@@_internal_b_int}
+% 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.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_uniformdeviate:D
+ {
+ \int_new:N \l_@@_internal_a_int
+ \int_new:N \l_@@_internal_b_int
+ \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
+ {
+ \__kernel_msg_error:nnx { kernel } { shuffle-too-large }
+ { \token_to_str:N #2 }
+ }
+ {
+ \group_begin:
+ \cs_set_eq:NN \@@_item:n \@@_shuffle_item:n
+ \int_zero:N \l_@@_internal_a_int
+ #2
+ \seq_gset_from_inline_x:Nnn \g_@@_internal_seq
+ { \int_step_function:nN { \l_@@_internal_a_int } }
+ { \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
+ }
+ \__kernel_msg_new:nnnn { kernel } { 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.
+ }
+ }
+ {
+ \cs_new_protected:Npn \seq_shuffle:N #1
+ {
+ \__kernel_msg_error:nnn { kernel } { fp-no-random }
+ { \seq_shuffle:N #1 }
+ }
+ \cs_new_eq:NN \seq_gshuffle:N \seq_shuffle:N
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_indexed_map_function:NN, \seq_indexed_map_inline:Nn,
+% \@@_indexed_map:nNN, \@@_indexed_map:Nw
+% }
+% Similar to \cs{seq_map_function:NN} but we keep track of the item
+% index as a |;|-delimited argument of \cs{@@_indexed_map:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \seq_indexed_map_function:NN #1#2
+ {
+ \@@_indexed_map:NN #1#2
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new_protected:Npn \seq_indexed_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 {#2}
+ \exp_args:NNc \@@_indexed_map: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 \@@_indexed_map:NN #1#2
+ {
+ \exp_after:wN \@@_indexed_map: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 \@@_indexed_map:Nw #1#2 ; #3 \@@_item:n #4
+ {
+ #3
+ #1 {#2} {#4}
+ \exp_after:wN \@@_indexed_map:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 1 + #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3skip}}
+%
+% \begin{macrocode}
+%<@@=dim>
+% \end{macrocode}
+%
+% \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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_sign:n } }
+\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}
+%
+% \subsection{Additions to \pkg{l3sys}}
+%
+% \begin{macrocode}
+%<@@=sys>
+% \end{macrocode}
+%
+% \begin{variable}{\c_sys_engine_version_str}
+% Various different engines, various different ways to extract the
+% data!
+% \begin{macrocode}
+\str_const:Nx \c_sys_engine_version_str
+ {
+ \str_case:on \c_sys_engine_str
+ {
+ { pdftex }
+ {
+ \fp_eval:n { round(\int_use:N \tex_pdftexversion:D / 100 , 2) }
+ .
+ \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 }
+ {
+ \fp_eval:n { round(\int_use:N \tex_luatexversion:D / 100, 2) }
+ .
+ \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}
+%
+% \begin{macro}[EXP]{\sys_rand_seed:}
+% Unpack the primitive. When random numbers are not available, we
+% return zero after an error (and incidentally make sure the number of
+% expansions needed is the same as with random numbers available).
+% \begin{macrocode}
+\sys_if_rand_exist:TF
+ { \cs_new:Npn \sys_rand_seed: { \tex_the:D \tex_randomseed:D } }
+ {
+ \cs_new:Npn \sys_rand_seed:
+ {
+ \int_value:w
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-no-random }
+ { \sys_rand_seed: }
+ \c_zero_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sys_gset_rand_seed:n}
+% The primitive always assigns the seed globally.
+% \begin{macrocode}
+\sys_if_rand_exist:TF
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ { \tex_setrandomseed:D \int_eval:n {#1} \exp_stop_f: }
+ }
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ {
+ \__kernel_msg_error:nnn { kernel } { fp-no-random }
+ { \sys_gset_rand_seed:n {#1} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_sys_shell_escape_int}
+% Expose the engine's shell escape status to the user.
+% \begin{macrocode}
+\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{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:Nx \c_@@_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\sys_shell_get:nnN}
+% \begin{macro}{\sys_shell_get: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_shell_get:nnN #1#2#3
+ {
+ \sys_shell_get:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\prg_new_protected_conditional:Npnn \sys_shell_get: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} { " }
+ {
+ \__kernel_msg_error:nnx
+ { 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}
+ }
+\__kernel_msg_new:nnnn { kernel } { quote-in-shell }
+ { Quotes~in~shell~command~'#1'. }
+ { Shell~commands~cannot~contain~quotes~("). }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \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.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \str_const:Nx \c_sys_platform_str
+ { \lua_now:n { 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{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}
+\@@_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}
+%
+% \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}
+% Execute commands through shell escape immediately.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ {
+ \lua_now:e
+ { os.execute(" \lua_escape:e { \tl_to_str: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 { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sys_shell_shipout:n}
+% Execute commands through shell escape at shipout.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ {
+ \lua_shipout_e:n
+ { os.execute(" \lua_escape:e { \tl_to_str: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 { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3file}}
+%
+% \begin{macrocode}
+%<@@=file>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_str_cmp:nn}
+% \begin{macro}[EXP]{\@@_str_escape:n}
+% 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:Npn \@@_str_cmp:nn #1#2 { \tex_strcmp:D {#1} {#2} }
+\sys_if_engine_luatex:T
+ {
+ \cs_set:Npn \@@_str_cmp:nn #1#2
+ {
+ \lua_now:e
+ {
+ l3kernel.strcmp
+ (
+ " \@@_str_escape:n {#1}",
+ " \@@_str_escape:n {#2}"
+ )
+ }
+ }
+ \cs_new:Npn \@@_str_escape:n #1
+ {
+ \lua_escape:e
+ { \__kernel_tl_to_str:w \use:e { {#1} } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_first_tl, \l_@@_second_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_first_tl
+\tl_new:N \l_@@_second_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\file_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. This could be expandable but that would
+% skip any file look-up: to stay consistent we retain that at the cost of
+% expansion.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \file_compare_timestamp:nNn #1#2#3
+ { T , F , TF }
+ {
+ \group_begin:
+ \file_get_full_name:nNTF {#1} \l_@@_first_tl
+ {
+ \file_get_full_name:nNTF {#3} \l_@@_second_tl
+ {
+ \if_int_compare:w
+ \@@_str_cmp:nn
+ { \@@_timestamp:n \exp_after:wN { \l_@@_first_tl } }
+ { \@@_timestamp:n \exp_after:wN { \l_@@_second_tl } }
+ #2 0 \exp_stop_f:
+ \group_end:
+ \prg_return_true:
+ \else:
+ \group_end:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \group_end:
+ \if_charcode:w #2 >
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+ {
+ \group_end:
+ \if_charcode:w #2 <
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_timestamp:n #1
+ {
+ \lua_now:e
+ { l3kernel.filemoddate ( " \lua_escape:e {#1} " ) }
+ }
+ }
+ { \cs_new_eq:NN \@@_timestamp:n \tex_filemoddate:D }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=ior>
+% \end{macrocode}
+%
+% \begin{macro}{\ior_shell_open:Nn}
+% \begin{macro}{\@@_shell_open:nN}
+% Actually much easier than either the standard open or input versions!
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \exp_args:No \@@_shell_open:nN { \tl_to_str:n {#2} } #1 }
+ { \__kernel_msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \@@_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_ior_open:Nn #2 { "|#1" } }
+ }
+\__kernel_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}
+%
+% \subsection{Additions to \pkg{l3tl}}
+%
+% \subsubsection{Unicode case changing}
+%
+% The mechanisms needed for case changing are somewhat involved, particularly
+% to allow for all of the special cases. These functions also require the
+% appropriate data extracted from the Unicode documentation (either manually
+% or automatically).
+%
+% First, some code which \enquote{belongs} in \pkg{l3tokens} but has to come
+% here.
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \char_lower_case:N, \char_upper_case:N,
+% \char_mixed_case:N, \char_fold_case:N
+% }
+% \begin{macro}[EXP]{\@@_change_case:nNN}
+% \begin{macro}[EXP]{\@@_change_case:nN}
+% \begin{macro}[EXP]{\@@_change_case_multi:n, \@@_change_case_multi:v}
+% \begin{macro}[EXP]{\@@_change_case_multi:NNNw}
+% \begin{macro}[EXP]{\@@_change_case:NN}
+% \begin{macro}[EXP]{\@@_change_case:NNN}
+% \begin{macro}[EXP]{\@@_change_case:N}
+% \begin{macro}[EXP]
+% {
+% \char_str_lower_case:N, \char_str_upper_case:N,
+% \char_str_mixed_case:N, \char_str_fold_case:N
+% }
+% \begin{macro}[EXP]{\@@_str_change_case:nNN}
+% \begin{macro}[EXP]{\@@_str_change_case:nN}
+% Expandable character generation is done using a two-part approach.
+% First, see if the current character has a special mapping for the current
+% transformation. If it does, insert that. Otherwise, use the \TeX{} data
+% to look up the one-to-one mapping, and generate the appropriate character
+% with the appropriate category code. Mixed case needs an extra step as it
+% may be special-cased or might be a special upper case outcome. The internal
+% when using non-Unicode engines has to be set up to only do anything
+% with ASCII characters.
+%
+% To ensure that the category codes produced are predictable, every character
+% is re-generated even if it is otherwise unchanged. This makes life a little
+% interesting when we might have multiple output characters: we have to
+% grab each of them and case change them in reverse order to maintain
+% \texttt{f}-type expandability.
+% \begin{macrocode}
+\cs_new:Npn \char_lower_case:N #1
+ { \@@_change_case:nNN { lower } \char_value_lccode:n #1 }
+\cs_new:Npn \char_upper_case:N #1
+ { \@@_change_case:nNN { upper } \char_value_uccode:n #1 }
+\cs_new:Npn \char_mixed_case:N #1
+ {
+ \tl_if_exist:cTF { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ {
+ \@@_change_case_multi:v
+ { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ }
+ { \char_upper_case:N #1 }
+ }
+\cs_new:Npn \char_fold_case:N #1
+ { \@@_change_case:nNN { fold } \char_value_lccode:n #1 }
+\cs_new:Npn \@@_change_case:nNN #1#2#3
+ {
+ \tl_if_exist:cTF { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ {
+ \@@_change_case_multi:v
+ { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ }
+ { \exp_args:Nf \@@_change_case:nN { #2 { `#3 } } #3 }
+ }
+\cs_new:Npn \@@_change_case:nN #1#2
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \@@_change_case:N #2 }
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+\cs_new:Npn \@@_change_case_multi:n #1
+ { \@@_change_case_multi:NNNw #1 \q_no_value \q_no_value \q_stop }
+\cs_new:Npn \@@_change_case_multi:NNNw #1#2#3#4 \q_stop
+ {
+ \quark_if_no_value:NTF #3
+ {
+ \quark_if_no_value:NTF #2
+ { \@@_change_case:N #1 }
+ { \@@_change_case:NN #1#2 }
+ }
+ { \@@_change_case:NNN #1#2#3 }
+ }
+\cs_generate_variant:Nn \@@_change_case_multi:n { v }
+\cs_new:Npn \@@_change_case:NN #1#2
+ {
+ \exp_args:Nnf \use:nn
+ { \@@_change_case:N #1 }
+ { \@@_change_case:N #2 }
+ }
+\cs_new:Npn \@@_change_case:NNN #1#2#3
+ {
+ \exp_args:Nnff \use:nnn
+ { \@@_change_case:N #1 }
+ { \@@_change_case:N #2 }
+ { \@@_change_case:N #3 }
+ }
+\cs_new:Npn \@@_change_case:N #1
+ { \char_generate:nn { `#1 } { \char_value_catcode:n { `#1 } } }
+\bool_lazy_or:nnF { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: }
+ {
+ \cs_set_eq:NN \@@_change_case:nN \use_ii:nn
+ }
+% \end{macrocode}
+% Same story for the string version, except category code is easier
+% to follow. This of course makes this version significantly faster.
+% \begin{macrocode}
+\cs_new:Npn \char_str_lower_case:N #1
+ { \@@_str_change_case:nNN { lower } \char_value_lccode:n #1 }
+\cs_new:Npn \char_str_upper_case:N #1
+ { \@@_str_change_case:nNN { upper } \char_value_uccode:n #1 }
+\cs_new:Npn \char_str_mixed_case:N #1
+ {
+ \tl_if_exist:cTF { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ { \tl_to_str:c { c_@@_mixed_case_ \token_to_str:N #1 _tl } }
+ { \char_str_upper_case:N #1 }
+ }
+\cs_new:Npn \char_str_fold_case:N #1
+ { \@@_str_change_case:nNN { fold } \char_value_lccode:n #1 }
+\cs_new:Npn \@@_str_change_case:nNN #1#2#3
+ {
+ \tl_if_exist:cTF { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ { \tl_to_str:c { c_@@_ #1 _case_ \token_to_str:N #3 _tl } }
+ { \exp_args:Nf \@@_str_change_case:nN { #2 { `#3 } } #3 }
+ }
+\cs_new:Npn \@@_str_change_case:nN #1#2
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \tl_to_str:n {#2} }
+ { \char_generate:nn {#1} { 12 } }
+ }
+\bool_lazy_or:nnF { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: }
+ {
+ \cs_set:Npn \@@_str_change_case:nN #1#2
+ { \tl_to_str:n {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\char_codepoint_to_bytes:n}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxi:n}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxii:Nnn}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxiii:n}
+% \begin{macro}[EXP]
+% {
+% \@@_codepoint_to_bytes_outputi:nw ,
+% \@@_codepoint_to_bytes_outputii:nw ,
+% \@@_codepoint_to_bytes_outputiii:nw ,
+% \@@_codepoint_to_bytes_outputiv:nw
+% }
+% \begin{macro}[EXP]
+% {\@@_codepoint_to_bytes_output:nnn, \@@_codepoint_to_bytes_output:fnn}
+% \begin{macro}[EXP]{\@@_codepoint_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 \char_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: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, documented-as=\tl_if_head_eq_catcode:nNTF]
+% {\tl_if_head_eq_catcode:oNTF}
+% Extra variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_if_head_eq_catcode:nNTF { o }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n}
+% \begin{macro}[EXP]{\tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn}
+% The user level functions here are all wrappers around the internal
+% functions for case changing.
+% \begin{macrocode}
+\cs_new:Npn \tl_lower_case:n { \@@_change_case:nnn { lower } { } }
+\cs_new:Npn \tl_upper_case:n { \@@_change_case:nnn { upper } { } }
+\cs_new:Npn \tl_mixed_case:n { \@@_change_case:nnn { mixed } { } }
+\cs_new:Npn \tl_lower_case:nn { \@@_change_case:nnn { lower } }
+\cs_new:Npn \tl_upper_case:nn { \@@_change_case:nnn { upper } }
+\cs_new:Npn \tl_mixed_case:nn { \@@_change_case:nnn { mixed } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case:nnn}
+% \begin{macro}[EXP]{\@@_change_case_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_loop:wnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_output:nwn ,
+% \@@_change_case_output:Vwn ,
+% \@@_change_case_output:own ,
+% \@@_change_case_output:vwn ,
+% \@@_change_case_output:fwn ,
+% }
+% \begin{macro}[EXP]{\@@_change_case_end:wn}
+% \begin{macro}[EXP]{\@@_change_case_group:nwnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_group_lower:nnnn ,
+% \@@_change_case_group_upper:nnnn ,
+% \@@_change_case_group_mixed:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_space:wnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:Nwnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:NNNnnn}
+% \begin{macro}[EXP]{\@@_change_case_math:NNNnnn}
+% \begin{macro}[EXP]{\@@_change_case_math_loop:wNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math:NwNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math_group:nwNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math_space:wNNnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:Nnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_char_lower:Nnn ,
+% \@@_change_case_char_upper:Nnn ,
+% \@@_change_case_char_mixed:Nnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_char:nN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nn}
+% \begin{macro}[EXP]{\@@_change_case_cs_letterlike:Nn}
+% \begin{macro}[EXP]{\@@_change_case_cs_letterlike:NnN}
+% \begin{macro}[EXP]{\@@_change_case_cs_accents:NN}
+% \begin{macro}[EXP]{\@@_change_case_cs:N}
+% \begin{macro}[EXP]{\@@_change_case_cs:NN}
+% \begin{macro}[EXP]{\@@_change_case_cs:NNn}
+% \begin{macro}[EXP]{\@@_change_case_protect:wNN}
+% \begin{macro}[EXP]{\@@_change_case_if_expandable:NTF}
+% \begin{macro}[EXP]{\@@_change_case_cs_expand:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_cs_expand:NN}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip:N}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip:NN}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip_tidy:Nwn}
+% \begin{macro}[EXP]{\@@_change_case_mixed_switch:w}
+% The mechanism for the core conversion of case is based on the idea that
+% we can use a loop to grab the entire token list plus a quark: the latter is
+% used as an end marker and to avoid any brace stripping. Depending on the
+% nature of the first item in the grabbed argument, it can either processed
+% as a single token, treated as a group or treated as a space. These
+% different cases all work by re-reading |#1| in the appropriate way, hence
+% the repetition of |#1 \q_recursion_stop|.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case:nnn #1#2#3
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_change_case_aux:nnn #1#2#3
+ {
+ \group_align_safe_begin:
+ \@@_change_case_loop:wnn
+ #3 \q_recursion_tail \q_recursion_stop {#1} {#2}
+ \@@_change_case_result:n { }
+ }
+\cs_new:Npn \@@_change_case_loop:wnn #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_N_type:Nwnn }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_change_case_group:nwnn }
+ { \@@_change_case_space:wnn }
+ }
+ #1 \q_recursion_stop
+ }
+% \end{macrocode}
+% Earlier versions of the code where only \texttt{x}-type expandable rather
+% than \texttt{f}-type: this causes issues with nesting and so the slight
+% performance hit is taken for a better outcome in usability terms. Setting
+% up for \texttt{f}-type expandability has two requirements: a marker
+% token after the main loop (see above) and a mechanism to \enquote{load}
+% and finalise the result. That is handled in the code below, which includes
+% the necessary material to end the \cs{exp:w} expansion.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_output:nwn #1#2 \@@_change_case_result:n #3
+ { #2 \@@_change_case_result:n { #3 #1 } }
+\cs_generate_variant:Nn \@@_change_case_output:nwn { V , o , v , f }
+\cs_new:Npn \@@_change_case_end:wn #1 \@@_change_case_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+% \end{macrocode}
+% Handling for the cases where the current argument is a brace group or
+% a space is relatively easy. For the brace case, the routine works
+% recursively, using the expandability of the mechanism to ensure that the
+% result is finalised before storage. For the space case it is simply a
+% question of removing the space in the input and storing it in the output.
+% In both cases, and indeed for the \texttt{N}-type grabber, after removing
+% the current item from the input \cs{@@_change_case_loop:wnn} is inserted
+% in front of the remaining tokens.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_group:nwnn #1#2 \q_recursion_stop #3#4
+ {
+ \use:c { @@_change_case_group_ #3 : nnnn } {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_group_lower:nnnn #1#2#3#4
+ {
+ \@@_change_case_output:own
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#3} {#4} {#1}
+ }
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
+ }
+\cs_new_eq:NN \@@_change_case_group_upper:nnnn
+ \@@_change_case_group_lower:nnnn
+% \end{macrocode}
+% For the \enquote{mixed} case, a group is taken as forcing a switch to lower
+% casing. That means we need a separate auxiliary. (Tracking whether we have
+% found a first character inside a group and transferring the information out
+% looks pretty horrible.)
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_group_mixed:nnnn #1#2#3#4
+ {
+ \@@_change_case_output:own
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#3} {#4} {#1}
+ }
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#4}
+ }
+\exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_space:wnn \c_space_tl
+ {
+ \@@_change_case_output:nwn { ~ }
+ \@@_change_case_loop:wnn
+ }
+% \end{macrocode}
+% For \texttt{N}-type arguments there are several stages to the approach.
+% First, a simply check for the end-of-input marker, which if found triggers
+% the final clean up and output step. Assuming that is not the case, the
+% first check is for math-mode escaping: this test can encompass control
+% sequences or other \texttt{N}-type tokens so is handled up front.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:Nwnn #1#2 \q_recursion_stop
+ {
+ \quark_if_recursion_tail_stop_do:Nn #1
+ { \@@_change_case_end:wn }
+ \exp_after:wN \@@_change_case_N_type:NNNnnn
+ \exp_after:wN #1 \l_tl_case_change_math_tl
+ \q_recursion_tail ? \q_recursion_stop {#2}
+ }
+% \end{macrocode}
+% Looking for math mode escape first requires a loop over the possible
+% token pairs to see if the current input (|#1|) matches an open-math case
+% (|#2|). If if does then this test loop is ended and a new input-gathering
+% one is begun. The latter simply transfers material from the input to the
+% output without any expansion, testing each \texttt{N}-type token to see
+% if it matches the close-math case required. If that is the situation then
+% the \enquote{math loop} stops and resumes the main loop: as that might
+% be either the standard case-changing one or the mixed-case alternative,
+% it is not hard-coded into the math loop but is rather passed as argument
+% |#3| to \cs{@@_change_case_math:NNNnnn}. If no close-math token is found
+% then the final clean-up is forced (\emph{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:NNNnnn #1#2#3
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_N_type:Nnnn #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_math:NNNnnn
+ #1 #3 \@@_change_case_loop:wnn
+ }
+ }
+ { \@@_change_case_N_type:NNNnnn #1 }
+ }
+\cs_new:Npn \@@_change_case_math:NNNnnn #1#2#3#4
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_math_loop:wNNnn #4 \q_recursion_stop #2 #3
+ }
+\cs_new:Npn \@@_change_case_math_loop:wNNnn #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_math:NwNNnn }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_change_case_math_group:nwNNnn }
+ { \@@_change_case_math_space:wNNnn }
+ }
+ #1 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_math:NwNNnn #1#2 \q_recursion_stop #3#4
+ {
+ \token_if_eq_meaning:NNTF \q_recursion_tail #1
+ { \@@_change_case_end:wn }
+ {
+ \@@_change_case_output:nwn {#1}
+ \token_if_eq_meaning:NNTF #1 #3
+ { #4 #2 \q_recursion_stop }
+ { \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop #3#4 }
+ }
+ }
+\cs_new:Npn \@@_change_case_math_group:nwNNnn #1#2 \q_recursion_stop
+ {
+ \@@_change_case_output:nwn { {#1} }
+ \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \@@_change_case_math_space:wNNnn \c_space_tl
+ {
+ \@@_change_case_output:nwn { ~ }
+ \@@_change_case_math_loop:wNNnn
+ }
+% \end{macrocode}
+% Once potential math-mode cases are filtered out the next stage is to
+% test if the token grabbed is a control sequence: they cannot be used in
+% the lookup table and also may require expansion. At this stage the loop
+% code starting \cs{@@_change_case_loop:wnn} is inserted: all subsequent
+% steps in the code which need a look-ahead are coded to rely on this and
+% thus have \texttt{w}-type arguments if they may do a look-ahead.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:Nnnn #1#2#3#4
+ {
+ \token_if_cs:NTF #1
+ { \@@_change_case_cs_letterlike:Nn #1 {#3} }
+ { \use:c { @@_change_case_char_ #3 :Nnn } #1 {#3} {#4} }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
+ }
+% \end{macrocode}
+% For character tokens there are some special cases to deal with then
+% the majority of changes are covered by using the \TeX{} data as a lookup
+% along with expandable character generation. This avoids needing a very
+% large number of macros or (as seen in earlier versions) a somewhat tricky
+% split of the characters into various blocks. Notice that the special case
+% code may do a look-ahead so requires a final \texttt{w}-type argument
+% whereas the core lookup table does not and also guarantees an output so
+% \texttt{f}-type expansion may be used to obtain the case-changed result.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_char_lower:Nnn #1#2#3
+ {
+ \cs_if_exist_use:cF { @@_change_case_ #2 _ #3 :Nnw }
+ { \use_ii:nn }
+ #1
+ {
+ \use:c { @@_change_case_ #2 _ sigma:Nnw } #1
+ { \@@_change_case_char:nN {#2} #1 }
+ }
+ }
+\cs_new_eq:NN \@@_change_case_char_upper:Nnn
+ \@@_change_case_char_lower:Nnn
+% \end{macrocode}
+% For mixed case, the code is somewhat different: there is a need to
+% look up both mixed and upper case chars and we have to cover the
+% situation where there is a character to skip over.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_char_mixed:Nnn #1#2#3
+ {
+ \@@_change_case_mixed_switch:w
+ \cs_if_exist_use:cF { @@_change_case_mixed_ #3 :Nnw }
+ {
+ \cs_if_exist_use:cF { @@_change_case_upper_ #3 :Nnw }
+ { \use_ii:nn }
+ }
+ #1
+ { \@@_change_case_mixed_skip:N #1 }
+ }
+% \end{macrocode}
+% For Unicode engines we can handle all characters directly. However, for
+% the $8$-bit engines the aim is to deal with (a subset of) Unicode (UTF-8)
+% input. They deal with that by making the upper half of the range active,
+% so we look for that and if found work out how many UTF-8 octets there
+% are to deal with. Those can then be grabbed to reconstruct the full
+% Unicode character, which is then used in a lookup. (As will become
+% obvious below, there is no intention here of covering all of Unicode.)
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_char:nN #1#2
+ {
+ \@@_change_case_output:fwn
+ { \use:c { char_ #1 _case:N } #2 }
+ }
+ }
+ {
+ \cs_new:Npn \@@_change_case_char:nN #1#2
+ {
+ \int_compare:nNnTF { `#2 } > { "80 }
+ {
+ \int_compare:nNnTF { `#2 } < { "E0 }
+ { \@@_change_case_char_UTFviii:nNNN {#1} #2 }
+ {
+ \int_compare:nNnTF { `#2 } < { "F0 }
+ { \@@_change_case_char_UTFviii:nNNNN {#1} #2 }
+ { \@@_change_case_char_UTFviii:nNNNNN {#1} #2 }
+ }
+ }
+ {
+ \@@_change_case_output:fwn
+ { \use:c { char_ #1 _case:N } #2 }
+ }
+ }
+ }
+% \end{macrocode}
+% To allow for the special case of mixed case, we insert here a
+% action-dependent auxiliary.
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNN #1#2#3#4
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNNN #1#2#3#4#5
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4#5} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNNNN #1#2#3#4#5#6
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4#5#6} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nnN #1#2#3
+ {
+ \cs_if_exist:cTF { c_@@_ #1 _case_ \tl_to_str:n {#2} _tl }
+ {
+ \@@_change_case_output:vwn
+ { c_@@_ #1 _case_ \tl_to_str:n {#2} _tl }
+ }
+ { \@@_change_case_output:nwn {#2} }
+ #3
+ }
+ }
+% \end{macrocode}
+% Before dealing with general control sequences there are the special
+% ones to deal with. Letter-like control sequences are a simple look-up,
+% while for accents the loop is much as done elsewhere. Notice that
+% we have a no-op test to make sure there is no unexpected expansion of
+% letter-like input. The split into two parts here allows us to insert
+% the \enquote{switch} code for mixed casing.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs_letterlike:Nn #1#2
+ {
+ \str_if_eq:nnTF {#2} { mixed }
+ {
+ \@@_change_case_cs_letterlike:NnN #1 { upper }
+ \@@_change_case_mixed_switch:w
+ }
+ { \@@_change_case_cs_letterlike:NnN #1 {#2} \prg_do_nothing: }
+ }
+\cs_new:Npn \@@_change_case_cs_letterlike:NnN #1#2#3
+ {
+ \cs_if_exist:cTF { c_@@_change_case_ #2 _ \token_to_str:N #1 _tl }
+ {
+ \@@_change_case_output:vwn
+ { c_@@_change_case_ #2 _ \token_to_str:N #1 _tl }
+ #3
+ }
+ {
+ \cs_if_exist:cTF
+ {
+ c_@@_change_case_
+ \str_if_eq:nnTF {#2} { lower } { upper } { lower }
+ _ \token_to_str:N #1 _tl
+ }
+ {
+ \@@_change_case_output:nwn {#1}
+ #3
+ }
+ {
+ \exp_after:wN \@@_change_case_cs_accents:NN
+ \exp_after:wN #1 \l_tl_case_change_accents_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+ }
+ }
+\cs_new:Npn \@@_change_case_cs_accents:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_cs:N #1 }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_output:nwn {#1} }
+ }
+ { \@@_change_case_cs_accents:NN #1 }
+ }
+% \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: that comes \emph{after} the loop function which
+% is therefore rearranged. In a \LaTeXe{} context, \tn{protect} needs
+% to be treated specially, to prevent expansion of the next token but
+% output it without braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs:N #1
+ {
+%<*package>
+ \str_if_eq:nnTF {#1} { \protect } { \@@_change_case_protect:wNN }
+%</package>
+ \exp_after:wN \@@_change_case_cs:NN
+ \exp_after:wN #1 \l_tl_case_change_exclude_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_cs:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ {
+ \@@_change_case_cs_expand:Nnw #1
+ { \@@_change_case_output:nwn {#1} }
+ }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_cs:NNn #1 }
+ }
+ { \@@_change_case_cs:NN #1 }
+ }
+\cs_new:Npn \@@_change_case_cs:NNn #1#2#3
+ {
+ \@@_change_case_output:nwn { #1 {#3} }
+ #2
+ }
+%<*package>
+\cs_new:Npn \@@_change_case_protect:wNN #1 \q_recursion_stop #2 #3
+ { \@@_change_case_output:nwn { \protect #3 } #2 }
+%</package>
+% \end{macrocode}
+% When a control sequence is not on the exclude list the other test if
+% to see if it is expandable. Once again, if there is a hit then the loop
+% function is grabbed as part of the clean-up and reinserted before the
+% now expanded material. The test for expandability has to check for
+% end-of-recursion as it is needed by the look-ahead code which might hit
+% the end of the input. The test is done in two parts as \cs{bool_if:nTF}
+% would choke if |#1| was |(|!
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_if_expandable:NTF #1
+ {
+ \token_if_expandable:NTF #1
+ {
+ \bool_lazy_any:nTF
+ {
+ { \token_if_eq_meaning_p:NN \q_recursion_tail #1 }
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ }
+ { \use_ii:nn }
+ { \use_i:nn }
+ }
+ { \use_ii:nn }
+ }
+\cs_new:Npn \@@_change_case_cs_expand:Nnw #1#2
+ {
+ \@@_change_case_if_expandable:NTF #1
+ { \@@_change_case_cs_expand:NN #1 }
+ { #2 }
+ }
+\cs_new:Npn \@@_change_case_cs_expand:NN #1#2
+ { \exp_after:wN #2 #1 }
+% \end{macrocode}
+% For mixed case, there is an additional list of exceptions to deal with:
+% once that is sorted, we can move on back to the main loop.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_mixed_skip:N #1
+ {
+ \exp_after:wN \@@_change_case_mixed_skip:NN
+ \exp_after:wN #1 \l_tl_mixed_case_ignore_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_mixed_skip:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:nn {#2}
+ { \@@_change_case_char:nN { mixed } #1 }
+ \int_compare:nNnT { `#1 } = { `#2 }
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_mixed_skip_tidy:Nwn
+ }
+ }
+ \@@_change_case_mixed_skip:NN #1
+ }
+\cs_new:Npn \@@_change_case_mixed_skip_tidy:Nwn #1#2 \q_recursion_stop #3
+ {
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { mixed }
+ }
+% \end{macrocode}
+% Needed to switch from mixed to lower casing when we have found a
+% first character in the former mode.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_mixed_switch:w
+ #1 \@@_change_case_loop:wnn #2 \q_recursion_stop #3
+ {
+ #1
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower }
+ }
+% \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}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:w}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_sigma:Nnw}
+% If the current char is an upper case 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.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_sigma:Nnw #1#2#3#4 \q_recursion_stop
+ {
+ \int_compare:nNnTF { `#1 } = { "03A3 }
+ {
+ \@@_change_case_output:fwn
+ { \@@_change_case_lower_sigma:w #4 \q_recursion_stop }
+ }
+ {#2}
+ #3 #4 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:w #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_lower_sigma:Nw #1 \q_recursion_stop }
+ { \c_@@_final_sigma_tl }
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:Nw #1#2 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #1
+ {
+ \exp_after:wN \@@_change_case_lower_sigma:w #1
+ #2 \q_recursion_stop
+ }
+ {
+ \token_if_letter:NTF #1
+ { \c_@@_std_sigma_tl }
+ { \c_@@_final_sigma_tl }
+ }
+ }
+% \end{macrocode}
+% Simply skip to the final step for upper casing.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_change_case_upper_sigma:Nnw \use_ii:nn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr_auxi:Nw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr_auxii:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_tr:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_az:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_az:Nnw}
+% 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}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_lower_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0049 }
+ { \@@_change_case_lower_tr_auxi:Nw }
+ {
+ \int_compare:nNnTF { `#1 } = { "0130 }
+ { \@@_change_case_output:nwn { i } }
+ {#2}
+ }
+ }
+% \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, which is done by the \cs{use_i:nn}
+% (it grabs \cs{@@_change_case_loop:wn} and the dot-above char and
+% discards the latter).
+% \begin{macrocode}
+ \cs_new:Npn \@@_change_case_lower_tr_auxi:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \@@_change_case_lower_tr_auxii:Nw #2 \q_recursion_stop }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ #1 #2 \q_recursion_stop
+ }
+ \cs_new:Npn \@@_change_case_lower_tr_auxii:Nw #1#2 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #1
+ {
+ \exp_after:wN \@@_change_case_lower_tr_auxi:Nw #1
+ #2 \q_recursion_stop
+ }
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_cs_p:N #1 }
+ { ! \int_compare_p:nNn { `#1 } = { "0307 } }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ {
+ \@@_change_case_output:nwn { i }
+ \use_i:nn
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% For $8$-bit engines, dot-above is not available so there is a simple
+% test for an upper-case I. Then we can look for the UTF-8 representation of
+% an upper case dotted-I without the combining char. If it's not there,
+% preserve the UTF-8 sequence as-is.
+% \begin{macrocode}
+ {
+ \cs_new:Npn \@@_change_case_lower_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0049 }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ {
+ \int_compare:nNnTF { `#1 } = { 196 }
+ { \@@_change_case_lower_tr_auxi:Nw #1 {#2} }
+ {#2}
+ }
+ }
+ \cs_new:Npn \@@_change_case_lower_tr_auxi:Nw #1#2#3#4
+ {
+ \int_compare:nNnTF { `#4 } = { 176 }
+ {
+ \@@_change_case_output:nwn { i }
+ #3
+ }
+ {
+ #2
+ #3 #4
+ }
+ }
+ }
+% \end{macrocode}
+% Upper casing is easier: just one exception with no context.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0069 }
+ { \@@_change_case_output:Vwn \c_@@_dotted_I_tl }
+ {#2}
+ }
+% \end{macrocode}
+% Straight copies.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_change_case_lower_az:Nnw \@@_change_case_lower_tr:Nnw
+\cs_new_eq:NN \@@_change_case_upper_az:Nnw \@@_change_case_upper_tr:Nnw
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:nNnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:Nw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:NNw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:NNw}
+% For Lithuanian, the issue to be dealt with is dots over lower case
+% letters: these should be present if there is another accent. That means
+% that there is some work to do when lower casing I and J. The first step
+% is a simple match attempt: \cs{c_@@_accents_lt_tl} contains
+% accented upper case letters which should gain a dot-above char in their
+% lower case form. This is done using \texttt{f}-type expansion so only one
+% pass is needed to find if it works or not. If there was no hit, the second
+% stage is to check for I, J and I-ogonek, and if the current char is a
+% match to look for a following accent.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt:Nnw #1
+ {
+ \exp_args:Nf \@@_change_case_lower_lt:nNnw
+ { \str_case:nVF #1 \c_@@_accents_lt_tl \exp_stop_f: }
+ #1
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nNnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \exp_args:Nf \@@_change_case_lower_lt:nnw
+ {
+ \int_case:nnF {`#2}
+ {
+ { "0049 } i
+ { "004A } j
+ { "012E } \c_@@_i_ogonek_tl
+ }
+ \exp_stop_f:
+ }
+ }
+ {
+ \@@_change_case_output:nwn {#1}
+ \use_none:n
+ }
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {#2}
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_lower_lt:Nw
+ }
+ }
+% \end{macrocode}
+% Grab the next char and see if it is one of the accents used in Lithuanian:
+% if it is, add the dot-above char into the output.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_lower_lt:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_lt:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_lower_lt:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnT
+ { ! \token_if_cs_p:N #2 }
+ {
+ \bool_lazy_any_p:n
+ {
+ { \int_compare_p:nNn { `#2 } = { "0300 } }
+ { \int_compare_p:nNn { `#2 } = { "0301 } }
+ { \int_compare_p:nNn { `#2 } = { "0303 } }
+ }
+ }
+ { \@@_change_case_output:Vwn \c_@@_dot_above_tl }
+ #1 #2#3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% For upper casing, the test required is for a dot-above char after an I,
+% J or I-ogonek. First a test for the appropriate letter, and if found a
+% look-ahead and potentially one token dropped.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_lt:Nnw #1
+ {
+ \exp_args:Nf \@@_change_case_upper_lt:nnw
+ {
+ \int_case:nnF {`#1}
+ {
+ { "0069 } I
+ { "006A } J
+ { "012F } \c_@@_I_ogonek_tl
+ }
+ \exp_stop_f:
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_lt:nnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {#2}
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_upper_lt:Nw
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_lt:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_upper_lt:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_lt:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_upper_lt:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #2 }
+ { \int_compare_p:nNn { `#2 } = { "0307 } }
+ { #1 }
+ { #1 #2 }
+ #3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_change_case_upper_de-alt:Nnw}
+% A simple alternative version for German.
+% \begin{macrocode}
+\cs_new:cpn { @@_change_case_upper_de-alt:Nnw } #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { 223 }
+ { \@@_change_case_output:Vwn \c_@@_upper_Eszett_tl }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_std_sigma_tl ,
+% \c_@@_final_sigma_tl ,
+% \c_@@_accents_lt_tl ,
+% \c_@@_dot_above_tl ,
+% \c_@@_upper_Eszett_tl
+% }
+% The above needs various special token lists containing pre-formed characters.
+% This set are only available in Unicode engines, with no-op definitions
+% for $8$-bit use.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \group_begin:
+ \cs_set:Npn \@@_tmp:n #1
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ \tl_const:Nx \c_@@_std_sigma_tl { \@@_tmp:n { "03C3 } }
+ \tl_const:Nx \c_@@_final_sigma_tl { \@@_tmp:n { "03C2 } }
+ \tl_const:Nx \c_@@_accents_lt_tl
+ {
+ \@@_tmp:n { "00CC }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0300 }
+ }
+ \@@_tmp:n { "00CD }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0301 }
+ }
+ \@@_tmp:n { "0128 }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0303 }
+ }
+ }
+ \tl_const:Nx \c_@@_dot_above_tl { \@@_tmp:n { "0307 } }
+ \tl_const:Nx \c_@@_upper_Eszett_tl { \@@_tmp:n { "1E9E } }
+ \group_end:
+ }
+ {
+ \tl_const:Nn \c_@@_std_sigma_tl { }
+ \tl_const:Nn \c_@@_final_sigma_tl { }
+ \tl_const:Nn \c_@@_accents_lt_tl { }
+ \tl_const:Nn \c_@@_dot_above_tl { }
+ \tl_const:Nn \c_@@_upper_Eszett_tl { }
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{variable}
+% {
+% \c_@@_dotless_i_tl ,
+% \c_@@_dotted_I_tl ,
+% \c_@@_i_ogonek_tl ,
+% \c_@@_I_ogonek_tl ,
+% }
+% For cases where there is an $8$-bit option in the |T1| font set up,
+% a variant is provided in both cases.
+% \begin{macrocode}
+\group_begin:
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \tl_const:Nx #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn
+ {"#2} { \char_value_catcode:n {"#2} }
+ }
+ }
+ }
+ {
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
+ {
+ \tl_const:Nx #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {##1} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {##2} { 13 }
+ }
+ }
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \char_codepoint_to_bytes:n {"#2} }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \group_end:
+ }
+ }
+ \@@_tmp:w \c_@@_dotless_i_tl { 0131 }
+ \@@_tmp:w \c_@@_dotted_I_tl { 0130 }
+ \@@_tmp:w \c_@@_i_ogonek_tl { 012F }
+ \@@_tmp:w \c_@@_I_ogonek_tl { 012E }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% For $8$-bit engines we now need to define the case-change data for
+% the multi-octet mappings. These need a list of what code points are
+% doable in |T1| so the list is hard coded (there's no saving in loading
+% the mappings dynamically). All of the straight-forward ones have two
+% octets, so that is taken as read.
+% \begin{macrocode}
+\group_begin:
+ \bool_lazy_or:nnT
+ { \sys_if_engine_pdftex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_set_protected:Npn \@@_loop:nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \tl_set:Nx \l_@@_internal_a_tl
+ {
+ \char_codepoint_to_bytes:n {"#1}
+ \char_codepoint_to_bytes:n {"#2}
+ }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \@@_loop:nn
+ }
+ \cs_set_protected:Npn \@@_tmp:nnnn #1#2#3#4#5
+ {
+ \tl_const:cx
+ {
+ c_@@_ #1 _case_
+ \char_generate:nn {#2} { 12 }
+ \char_generate:nn {#3} { 12 }
+ _tl
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#4} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#5} { 13 }
+ }
+ }
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
+ {
+ \tl_const:cx
+ {
+ c_@@_lower_case_
+ \char_generate:nn {#1} { 12 }
+ \char_generate:nn {#2} { 12 }
+ _tl
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#5} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#6} { 13 }
+ }
+ \@@_tmp:nnnn { upper } {#5} {#6} {#1} {#2}
+ \@@_tmp:nnnn { mixed } {#5} {#6} {#1} {#2}
+ }
+ \@@_loop:nn
+ { 00C0 } { 00E0 }
+ { 00C2 } { 00E2 }
+ { 00C3 } { 00E3 }
+ { 00C4 } { 00E4 }
+ { 00C5 } { 00E5 }
+ { 00C6 } { 00E6 }
+ { 00C7 } { 00E7 }
+ { 00C8 } { 00E8 }
+ { 00C9 } { 00E9 }
+ { 00CA } { 00EA }
+ { 00CB } { 00EB }
+ { 00CC } { 00EC }
+ { 00CD } { 00ED }
+ { 00CE } { 00EE }
+ { 00CF } { 00EF }
+ { 00D0 } { 00F0 }
+ { 00D1 } { 00F1 }
+ { 00D2 } { 00F2 }
+ { 00D3 } { 00F3 }
+ { 00D4 } { 00F4 }
+ { 00D5 } { 00F5 }
+ { 00D6 } { 00F6 }
+ { 00D8 } { 00F8 }
+ { 00D9 } { 00F9 }
+ { 00DA } { 00FA }
+ { 00DB } { 00FB }
+ { 00DC } { 00FC }
+ { 00DD } { 00FD }
+ { 00DE } { 00FE }
+ { 0100 } { 0101 }
+ { 0102 } { 0103 }
+ { 0104 } { 0105 }
+ { 0106 } { 0107 }
+ { 0108 } { 0109 }
+ { 010A } { 010B }
+ { 010C } { 010D }
+ { 010E } { 010F }
+ { 0110 } { 0111 }
+ { 0112 } { 0113 }
+ { 0114 } { 0115 }
+ { 0116 } { 0117 }
+ { 0118 } { 0119 }
+ { 011A } { 011B }
+ { 011C } { 011D }
+ { 011E } { 011F }
+ { 0120 } { 0121 }
+ { 0122 } { 0123 }
+ { 0124 } { 0125 }
+ { 0128 } { 0129 }
+ { 012A } { 012B }
+ { 012C } { 012D }
+ { 012E } { 012F }
+ { 0132 } { 0133 }
+ { 0134 } { 0135 }
+ { 0136 } { 0137 }
+ { 0139 } { 013A }
+ { 013B } { 013C }
+ { 013E } { 013F }
+ { 0141 } { 0142 }
+ { 0143 } { 0144 }
+ { 0145 } { 0146 }
+ { 0147 } { 0148 }
+ { 014A } { 014B }
+ { 014C } { 014D }
+ { 014E } { 014F }
+ { 0150 } { 0151 }
+ { 0152 } { 0153 }
+ { 0154 } { 0155 }
+ { 0156 } { 0157 }
+ { 0158 } { 0159 }
+ { 015A } { 015B }
+ { 015C } { 015D }
+ { 015E } { 015F }
+ { 0160 } { 0161 }
+ { 0162 } { 0163 }
+ { 0164 } { 0165 }
+ { 0168 } { 0169 }
+ { 016A } { 016B }
+ { 016C } { 016D }
+ { 016E } { 016F }
+ { 0170 } { 0171 }
+ { 0172 } { 0173 }
+ { 0174 } { 0175 }
+ { 0176 } { 0177 }
+ { 0178 } { 00FF }
+ { 0179 } { 017A }
+ { 017B } { 017C }
+ { 017D } { 017E }
+ { 01CD } { 01CE }
+ { 01CF } { 01D0 }
+ { 01D1 } { 01D2 }
+ { 01D3 } { 01D4 }
+ { 01E2 } { 01E3 }
+ { 01E6 } { 01E7 }
+ { 01E8 } { 01E9 }
+ { 01EA } { 01EB }
+ { 01F4 } { 01F5 }
+ { 0218 } { 0219 }
+ { 021A } { 021B }
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
+ {
+ \tl_const:cx
+ {
+ c_@@_ #3 _case_
+ \char_generate:nn {##1} { 12 }
+ \char_generate:nn {##2} { 12 }
+ _tl
+ }
+ {#2}
+ }
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \char_codepoint_to_bytes:n { "#1 } }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \group_end:
+ }
+ \@@_tmp:w { 00DF } { SS } { upper }
+ \@@_tmp:w { 00DF } { Ss } { mixed }
+ \@@_tmp:w { 0131 } { I } { upper }
+ }
+ \group_end:
+% \end{macrocode}
+%
+% 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_@@_change_case_lower_ \token_to_str:N #1 _tl }
+ { #2 }
+ \tl_const:cn { c_@@_change_case_upper_ \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_@@_change_case_upper_ \token_to_str:N \i _tl } { I }
+ \tl_const:cn { c_@@_change_case_upper_ \token_to_str:N \j _tl } { J }
+\group_end:
+% \end{macrocode}
+%
+% \begin{variable}{\l_tl_case_change_accents_tl}
+% A list of accents to leave alone.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_accents_tl
+\tl_set:Nn \l_tl_case_change_accents_tl
+ { \" \' \. \^ \` \~ \c \H \k \r \t \u \v }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:Nw}
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:NNw}
+% 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_mixed_nl:Nnw #1
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn { `#1 } = { `i } }
+ { \int_compare_p:nNn { `#1 } = { `I } }
+ {
+ \@@_change_case_output:nwn { I }
+ \@@_change_case_mixed_nl:Nw
+ }
+ }
+\cs_new:Npn \@@_change_case_mixed_nl:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_mixed_nl:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_mixed_nl:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_mixed_nl:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnTF
+ { ! ( \token_if_cs_p:N #2 ) }
+ {
+ \bool_lazy_or_p:nn
+ { \int_compare_p:nNn { `#2 } = { `j } }
+ { \int_compare_p:nNn { `#2 } = { `J } }
+ }
+ {
+ \@@_change_case_output:nwn { J }
+ #1
+ }
+ { #1 #2 }
+ #3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% The list of token pairs which are treated as math mode and so
+% not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_math_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_math_tl
+ { $ $ \( \) }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% The list of commands for which an argument is not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_exclude_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_exclude_tl
+ { \cite \ensuremath \label \ref }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% Characters to skip over when finding the first letter in a word to be
+% mixed cased.
+% \begin{macrocode}
+\tl_new:N \l_tl_mixed_case_ignore_tl
+\tl_set:Nx \l_tl_mixed_case_ignore_tl
+ {
+ ( % )
+ [ % ]
+ \cs_to_str:N \{ % \}
+ `
+ -
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Building a token list}
+%
+% 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:Npx} if the variable is local,
+% and \cs{cs_gset_nopar:Npx} 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:Npx} 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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_begin:N #1
+ { \@@_build_begin:NN \cs_set_nopar:Npx #1 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gbegin:N #1
+ { \@@_build_begin:NN \cs_gset_nopar:Npx #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_clear:N, \tl_build_gclear:N}
+% The |begin| and |gbegin| functions already clear enough to make the
+% token list variable effectively empty. Eventually the |begin| and
+% |gbegin| functions should check that |#1'| is empty or undefined,
+% while the |clear| and |gclear| functions ought to empty |#1'|,
+% |#1''| and so on, similar to \cs{tl_build_end:N}. This only affects
+% memory usage.
+% \begin{macrocode}
+\cs_new_eq:NN \tl_build_clear:N \tl_build_begin:N
+\cs_new_eq:NN \tl_build_gclear:N \tl_build_gbegin:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_build_put_right:Nn, \tl_build_put_right:Nx,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Nx,
+% \@@_build_last:NNn, \@@_build_put:nn, \@@_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:Npx} rather than
+% \cs[no-index]{tl_(g)set:Nx} 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
+% |x|-expansion of the second argument.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_right:Nn #1#2
+ {
+ \cs_set_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_right:Nx #1#2
+ {
+ \cs_set_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_right:Nn #1#2
+ {
+ \cs_gset_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_right:Nx #1#2
+ {
+ \cs_gset_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 }
+ }
+\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
+ {
+ \exp_after:wN \exp_not:n \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}
+%
+% \begin{macro}
+% {
+% \tl_build_put_left:Nn, \tl_build_put_left:Nx,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Nx, \@@_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
+% add the \meta{right} material after the \Arg{left} in the
+% |x|-expanding assignment.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_left:Nn #1
+ { \@@_build_put_left:NNn \cs_set_nopar:Npx #1 }
+\cs_generate_variant:Nn \tl_build_put_left:Nn { Nx }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_left:Nn #1
+ { \@@_build_put_left:NNn \cs_gset_nopar:Npx #1 }
+\cs_generate_variant:Nn \tl_build_gput_left:Nn { Nx }
+\cs_new_protected:Npn \@@_build_put_left:NNn #1#2#3
+ {
+ #1 #2
+ {
+ \exp_after:wN \exp_not:n \exp_after:wN
+ {
+ \exp:w \exp_after:wN \@@_build_put:nn
+ \exp_after:wN {#2} {#3}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_get:NN}
+% \begin{macro}{\@@_build_get:NNN, \@@_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 |x|-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 \tl_build_get:NN
+ { \@@_build_get:NNN \tl_set:Nx }
+\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
+ { \exp_after:wN \exp_not:n \exp_after:wN { \if_false: } \fi: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_end:N, \tl_build_gend:N, \@@_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_sr:N}. The local/global scope is checked by
+% \cs{tl_set:Nx} or \cs{tl_gset:Nx}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_end:N #1
+ {
+ \@@_build_get:NNN \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 \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}
+%
+% \subsubsection{Other additions to \pkg{l3tl}}
+%
+% \begin{macro}{\tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn}
+% \begin{macro}
+% {\tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn}
+% \begin{macro}
+% {
+% \@@_range_braced:w, \@@_range_collect_braced:w,
+% \@@_range_unbraced:w, \@@_range_collect_unbraced:w,
+% }
+% 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. The unbraced version is almost
+% identical. The version preserving braces and spaces starts by
+% deleting spaces before the argument to avoid collecting them, and
+% sets up \cs{@@_range_collect:nn} with a first argument of the form
+% |{| \Arg{collected} \meta{tokens} |}|, whose head is the collected
+% tokens and whose tail is what remains of the original token list.
+% This form makes it easier to move tokens to the \meta{collected}
+% tokens.
+% \begin{macrocode}
+\cs_new:Npn \tl_range_braced:Nnn { \exp_args:No \tl_range_braced:nnn }
+\cs_generate_variant:Nn \tl_range_braced:Nnn { c }
+\cs_new:Npn \tl_range_braced:nnn { \@@_range:Nnnn \@@_range_braced:w }
+\cs_new:Npn \tl_range_unbraced:Nnn
+ { \exp_args:No \tl_range_unbraced:nnn }
+\cs_generate_variant:Nn \tl_range_unbraced:Nnn { c }
+\cs_new:Npn \tl_range_unbraced:nnn
+ { \@@_range:Nnnn \@@_range_unbraced:w }
+\cs_new:Npn \@@_range_braced:w #1 ; #2
+ { \@@_range_collect_braced:w #1 ; { } #2 }
+\cs_new:Npn \@@_range_unbraced:w #1 ; #2
+ { \@@_range_collect_unbraced:w #1 ; { } #2 }
+\cs_new:Npn \@@_range_collect_braced:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > 1 \exp_stop_f:
+ \exp_after:wN \@@_range_collect_braced:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \fi:
+ { #2 {#3} }
+ }
+\cs_new:Npn \@@_range_collect_unbraced:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > 1 \exp_stop_f:
+ \exp_after:wN \@@_range_collect_unbraced:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \fi:
+ { #2 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3token}}
+%
+% \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{macrocode}
+%<@@=peek>
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_collect_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_collect_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \peek_catcode_collect_inline:Nn,
+% \peek_charcode_collect_inline:Nn,
+% \peek_meaning_collect_inline:Nn
+% }
+% \begin{macro}
+% {
+% \@@_collect:NNn, \@@_collect_true:w,
+% \@@_collect_remove:nw, \@@_collect:N
+% }
+% Most of the work is done by \cs{@@_execute_branches_\ldots{}:},
+% which calls either \cs{@@_true:w} or \cs{@@_false:w} according to
+% whether the next token \cs{l_peek_token} matches the search token
+% (stored in \cs{l_@@_search_token} and \cs{l_@@_search_tl}).
+% Here, in the \texttt{true} case we run \cs{@@_collect_true:w},
+% which generally calls \cs{@@_collect:N} to store the peeked token
+% into \cs{l_@@_collect_tl}, except in special non-\texttt{N}-type
+% cases (begin-group, end-group, or space), where a frozen token is
+% stored. The \texttt{true} branch calls
+% \cs{@@_execute_branches_\ldots{}:} to fetch more matching tokens.
+% Once there are no more, \cs{@@_false_aux:n} closes the safe-align
+% group and runs the user's inline code.
+% \begin{macrocode}
+\cs_new_protected:Npn \peek_catcode_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_catcode: }
+\cs_new_protected:Npn \peek_charcode_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_charcode: }
+\cs_new_protected:Npn \peek_meaning_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_meaning: }
+\cs_new_protected:Npn \@@_collect:NNn #1#2#3
+ {
+ \group_align_safe_begin:
+ \cs_set_eq:NN \l_@@_search_token #2
+ \tl_set:Nn \l_@@_search_tl {#2}
+ \tl_clear:N \l_@@_collect_tl
+ \cs_set:Npn \@@_false:w
+ { \exp_args:No \@@_false_aux:n \l_@@_collect_tl }
+ \cs_set:Npn \@@_false_aux:n ##1
+ {
+ \group_align_safe_end:
+ #3
+ }
+ \cs_set_eq:NN \@@_true:w \@@_collect_true:w
+ \cs_set:Npn \@@_true_aux:w { \peek_after:Nw #1 }
+ \@@_true_aux:w
+ }
+\cs_new_protected:Npn \@@_collect_true:w
+ {
+ \if_case:w
+ \if_catcode:w \exp_not:N \l_peek_token { 1 \exp_stop_f: \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } 2 \exp_stop_f: \fi:
+ \if_meaning:w \l_peek_token \c_space_token 3 \exp_stop_f: \fi:
+ 0 \exp_stop_f:
+ \exp_after:wN \@@_collect:N
+ \or: \@@_collect_remove:nw { \c_group_begin_token }
+ \or: \@@_collect_remove:nw { \c_group_end_token }
+ \or: \@@_collect_remove:nw { ~ }
+ \fi:
+ }
+\cs_new_protected:Npn \@@_collect:N #1
+ {
+ \tl_put_right:Nn \l_@@_collect_tl {#1}
+ \@@_true_aux:w
+ }
+\cs_new_protected:Npn \@@_collect_remove:nw #1
+ {
+ \tl_put_right:Nn \l_@@_collect_tl {#1}
+ \exp_after:wN \@@_true_remove:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3clist.dtx b/macros/latex/contrib/l3kernel/l3clist.dtx
new file mode 100644
index 0000000000..fa743396b7
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3clist.dtx
@@ -0,0 +1,2031 @@
+% \iffalse meta-comment
+%
+%% File: l3clist.dtx
+%
+% Copyright (C) 2004-2011 Frank Mittelbach, The LaTeX3 Project
+% (C) 2012-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Comma separated lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% Comma lists 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 comma list data.
+%
+% 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 items so the following gives an empty comma list:
+% \begin{verbatim}
+% \clist_clear_new:N \l_my_clist
+% \clist_put_right: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.
+%
+% 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). In addition, comma
+% lists cannot store quarks \cs{q_mark} or \cs{q_stop}. 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{comma list}
+% \end{syntax}
+% Creates a new \meta{comma list} or raises an error if the name is
+% already taken. The declaration is global. The \meta{comma list}
+% initially contains no items.
+% \end{function}
+%
+% \begin{function}[added = 2014-07-05]
+% {
+% \clist_const:Nn, \clist_const:Nx,
+% \clist_const:cn, \clist_const:cx
+% }
+% \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{comma list}
+% \end{syntax}
+% Clears all items from the \meta{comma list}.
+% \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{comma list}
+% \end{syntax}
+% Ensures that the \meta{comma list} 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{comma list_1} \meta{comma list_2}
+% \end{syntax}
+% Sets the content of \meta{comma list_1} equal to that of
+% \meta{comma list_2}.
+% \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{comma list} \meta{sequence}
+% \end{syntax}
+% Converts the data in the \meta{sequence} into a \meta{comma list}:
+% the original \meta{sequence} 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{comma list_1} \meta{comma list_2} \meta{comma list_3}
+% \end{syntax}
+% Concatenates the content of \meta{comma list_2} and \meta{comma list_3}
+% together and saves the result in \meta{comma list_1}. The items in
+% \meta{comma list_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{comma list}
+% \cs{clist_if_exist:NTF} \meta{comma list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{comma list} is currently defined. This does
+% not check that the \meta{comma list} 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:No, \clist_set:Nx,
+% \clist_set:cn, \clist_set:cV,
+% \clist_set:co, \clist_set:cx,
+% \clist_gset:Nn, \clist_gset:NV,
+% \clist_gset:No, \clist_gset:Nx,
+% \clist_gset:cn, \clist_gset:cV,
+% \clist_gset:co, \clist_gset:cx
+% }
+% \begin{syntax}
+% \cs{clist_set:Nn} \meta{comma list} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Sets \meta{comma list} 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{comma list} |{| \Arg{tokens} |}|.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-05]
+% {
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:No, \clist_put_left:Nx,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:co, \clist_put_left:cx,
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:No, \clist_gput_left:Nx,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \clist_gput_left:co, \clist_gput_left:cx
+% }
+% \begin{syntax}
+% \cs{clist_put_left:Nn} \meta{comma list} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Appends the \meta{items} to the left of the \meta{comma list}.
+% 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{comma list} |{| \Arg{tokens} |}|.
+% \end{function}
+%
+% \begin{function}[updated = 2011-09-05]
+% {
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:No, \clist_put_right:Nx,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:co, \clist_put_right:cx,
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:No, \clist_gput_right:Nx,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:co, \clist_gput_right:cx
+% }
+% \begin{syntax}
+% \cs{clist_put_right:Nn} \meta{comma list} |{|\meta{item_1},\ldots{},\meta{item_n}|}|
+% \end{syntax}
+% Appends the \meta{items} to the right of the \meta{comma list}.
+% 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{comma list} |{| \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{comma list}
+% \end{syntax}
+% Removes duplicate items from the \meta{comma list}, leaving the
+% left most copy of each item in the \meta{comma list}. The \meta{item}
+% comparison takes place on a token basis, as for \cs{tl_if_eq:nn(TF)}.
+% \begin{texnote}
+% This function iterates through every item in the \meta{comma list} 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{comma list} 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_gremove_all:Nn, \clist_gremove_all:cn
+% }
+% \begin{syntax}
+% \cs{clist_remove_all:Nn} \meta{comma list} \Arg{item}
+% \end{syntax}
+% Removes every occurrence of \meta{item} from the \meta{comma list}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nn(TF)}.
+% \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{comma list}
+% \end{syntax}
+% Reverses the order of items stored in the \meta{comma list}.
+% \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{x}-type or \texttt{e}-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{comma list}
+% \cs{clist_if_empty:NTF} \meta{comma list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{comma list} 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{comma list} 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{comma list} \Arg{item} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{item} is present in the \meta{comma list}.
+% 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 to 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}
+% \begin{syntax}
+% \cs{clist_map_function:NN} \meta{comma list} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} stored in the
+% \meta{comma list}. 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{comma list} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{comma list}. 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{comma list} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{comma 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{comma
+% list}, or its original value if there were no \meta{item}. The
+% \meta{items} are returned from left to right.
+% \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}
+% \begin{syntax}
+% \cs{clist_count:N} \meta{comma list}
+% \end{syntax}
+% Leaves the number of items in the \meta{comma list} in the input
+% stream as an \meta{integer denotation}. The total number of items
+% in a \meta{comma list} includes those which are duplicates,
+% \emph{i.e.}~every item in a \meta{comma list} 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{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{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{comma list} \meta{token list variable}
+% \end{syntax}
+% Stores the left-most item from a \meta{comma list} in the
+% \meta{token list variable} without removing it from the
+% \meta{comma list}. The \meta{token list variable} is assigned locally.
+% In the non-branching version, if the \meta{comma list} 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{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{comma list} 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{comma list} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{comma list} 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{comma list} 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{comma list} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{comma list} 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{comma list} is non-empty, pops the top item from the
+% \meta{comma list} in the \meta{token list variable}, \emph{i.e.}~removes
+% the item from the \meta{comma list}. Both the \meta{comma list} 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{comma list} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{comma list} 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{comma list} is non-empty, pops the top item from the
+% \meta{comma list} in the \meta{token list variable}, \emph{i.e.}~removes
+% the item from the \meta{comma list}. The \meta{comma list} 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:Nx,
+% \clist_push:cn, \clist_push:cV, \clist_push:co, \clist_push:cx,
+% \clist_gpush:Nn, \clist_gpush:NV, \clist_gpush:No, \clist_gpush:Nx,
+% \clist_gpush:cn, \clist_gpush:cV, \clist_gpush:co, \clist_gpush:cx
+% }
+% \begin{syntax}
+% \cs{clist_push:Nn} \meta{comma list} \Arg{items}
+% \end{syntax}
+% Adds the \Arg{items} to the top of the \meta{comma list}.
+% 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}
+% \begin{syntax}
+% \cs{clist_item:Nn} \meta{comma list} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{comma list} from~$1$ at the top (left), this
+% function evaluates the \meta{integer expression} and leaves the
+% appropriate item from the comma list in the input stream. If the
+% \meta{integer expression} is negative, indexing occurs from the
+% bottom (right) of the comma list. When the \meta{integer expression}
+% is larger than the number of items in the \meta{comma list} (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{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{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{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing comma lists}
+%
+% \begin{function}[updated = 2015-08-03]{\clist_show:N, \clist_show:c}
+% \begin{syntax}
+% \cs{clist_show:N} \meta{comma list}
+% \end{syntax}
+% Displays the entries in the \meta{comma list} 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 = 2015-08-03]{\clist_log:N, \clist_log:c}
+% \begin{syntax}
+% \cs{clist_log:N} \meta{comma list}
+% \end{syntax}
+% Writes the entries in the \meta{comma list} 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}
+%<*initex|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{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 , \q_recursion_tail , \q_recursion_stop
+ }
+\cs_new:Npn \@@_sanitize:Nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#2}
+ #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}
+% All \pkg{l3clist} functions go through the same test when they need
+% to determine whether to brace an item, so it is not a problem that
+% this test has false positives such as \enquote{\cs{q_mark}
+% \texttt{?}}. If the argument starts or end 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
+ \q_mark ? #1 ~ \q_mark ? ~ #1 \q_mark , ~ \q_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 \q_mark ? ~ #2 ~ \q_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:cn,
+% \clist_const:Nx, \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:Nx #1 { \@@_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_const:Nn { c , 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 \tl_set:Nx }
+\cs_new_protected:Npn \clist_gset_from_seq:NN
+ { \@@_set_from_seq:NNNN \clist_gclear:N \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 \tl_set:Nx }
+\cs_new_protected:Npn \clist_gconcat:NNN
+ { \@@_concat:NNNN \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:No, \clist_set:Nx,
+% \clist_set:cn, \clist_set:cV,
+% \clist_set:co, \clist_set:cx
+% }
+% \begin{macro}
+% {
+% \clist_gset:Nn, \clist_gset:NV,
+% \clist_gset:No, \clist_gset:Nx,
+% \clist_gset:cn, \clist_gset:cV,
+% \clist_gset:co, \clist_gset:cx
+% }
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_set:Nn #1#2
+ { \tl_set:Nx #1 { \@@_sanitize:n {#2} } }
+\cs_new_protected:Npn \clist_gset:Nn #1#2
+ { \tl_gset:Nx #1 { \@@_sanitize:n {#2} } }
+\cs_generate_variant:Nn \clist_set:Nn { NV , No , Nx , c , cV , co , cx }
+\cs_generate_variant:Nn \clist_gset:Nn { NV , No , Nx , c , cV , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_put_left:Nn, \clist_put_left:NV,
+% \clist_put_left:No, \clist_put_left:Nx,
+% \clist_put_left:cn, \clist_put_left:cV,
+% \clist_put_left:co, \clist_put_left:cx
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gput_left:Nn, \clist_gput_left:NV,
+% \clist_gput_left:No, \clist_gput_left:Nx,
+% \clist_gput_left:cn, \clist_gput_left:cV,
+% \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 , No , Nx }
+\cs_generate_variant:Nn \clist_put_left:Nn { c , cV , co , cx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_gput_left:Nn { c , cV , co , cx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_put_right:Nn, \clist_put_right:NV,
+% \clist_put_right:No, \clist_put_right:Nx,
+% \clist_put_right:cn, \clist_put_right:cV,
+% \clist_put_right:co, \clist_put_right:cx
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gput_right:Nn, \clist_gput_right:NV,
+% \clist_gput_right:No, \clist_gput_right:Nx,
+% \clist_gput_right:cn, \clist_gput_right:cV,
+% \clist_gput_right:co, \clist_gput_right:cx
+% }
+% \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 , No , Nx }
+\cs_generate_variant:Nn \clist_put_right:Nn { c , cV , co , cx }
+\cs_generate_variant:Nn \clist_gput_right:Nn { NV , No , Nx }
+\cs_generate_variant:Nn \clist_gput_right:Nn { c , cV , 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 , \q_stop #2
+ \fi:
+ }
+\cs_new_protected:Npn \@@_get:wN #1 , #2 \q_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{q_mark}, unless the original clist contained exactly one item:
+% then the argument is just \cs{q_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 \tl_set:Nx }
+\cs_new_protected:Npn \clist_gpop:NN
+ { \@@_pop:NNN \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 , \q_mark \q_stop #1#2#3
+ \fi:
+ }
+\cs_new_protected:Npn \@@_pop:wwNNN #1 , #2 \q_stop #3#4#5
+ {
+ \tl_set:Nn #5 {#1}
+ #3 #4
+ {
+ \@@_pop:wN \prg_do_nothing:
+ #2 \exp_not:o
+ , \q_mark \use_none:n
+ \q_stop
+ }
+ }
+\cs_new:Npn \@@_pop:wN #1 , \q_mark #2 #3 \q_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 , \q_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 \tl_set:Nx #1 #2 }
+\prg_new_protected_conditional:Npnn \clist_gpop:NN #1#2 { T , F , TF }
+ { \@@_pop_TF:NNN \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 , \q_mark \q_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_new_eq:NN \clist_push:NV \clist_put_left:NV
+\cs_new_eq:NN \clist_push:No \clist_put_left:No
+\cs_new_eq:NN \clist_push:Nx \clist_put_left:Nx
+\cs_new_eq:NN \clist_push:cn \clist_put_left:cn
+\cs_new_eq:NN \clist_push:cV \clist_put_left:cV
+\cs_new_eq:NN \clist_push:co \clist_put_left:co
+\cs_new_eq:NN \clist_push:cx \clist_put_left:cx
+\cs_new_eq:NN \clist_gpush:Nn \clist_gput_left:Nn
+\cs_new_eq:NN \clist_gpush:NV \clist_gput_left:NV
+\cs_new_eq:NN \clist_gpush:No \clist_gput_left:No
+\cs_new_eq:NN \clist_gpush:Nx \clist_gput_left:Nx
+\cs_new_eq:NN \clist_gpush:cn \clist_gput_left:cn
+\cs_new_eq:NN \clist_gpush:cV \clist_gput_left:cV
+\cs_new_eq:NN \clist_gpush:co \clist_gput_left:co
+\cs_new_eq:NN \clist_gpush:cx \clist_gput_left: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}
+ { \clist_put_right:Nn \l_@@_internal_remove_clist {##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}
+% \UnitTested
+% \begin{macro}{\clist_gremove_all:Nn, \clist_gremove_all:cn}
+% \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{q_mark} delimiter used is the one inserted by
+% \cs{@@_tmp:w}, and \cs{use_none_delimit_by_q_stop:w}
+% is deleted. At the end, the final \meta{item} is
+% grabbed, and the argument of \cs{@@_tmp:w} contains
+% \cs{q_mark}: in that case, \cs{@@_remove_all:w}
+% removes the second \cs{q_mark} (inserted by \cs{@@_tmp:w}),
+% and lets \cs{use_none_delimit_by_q_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 \tl_set:Nx }
+\cs_new_protected:Npn \clist_gremove_all:Nn
+ { \@@_remove_all:NNNn \clist_gset_from_seq:NN \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
+ , \q_mark , \use_none_delimit_by_q_stop:w ,
+ \@@_remove_all:
+ }
+ #2 #3
+ {
+ \exp_after:wN \@@_remove_all:
+ #3 , \q_mark , #4 , \q_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 , \q_mark , #2 , { \exp_not:n {#1} }
+\cs_generate_variant:Nn \clist_remove_all:Nn { c }
+\cs_generate_variant:Nn \clist_gremove_all:Nn { c }
+% \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{x}-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
+ { \tl_set:Nx #1 { \exp_args:No \clist_reverse:n {#1} } }
+\cs_new_protected:Npn \clist_greverse:N #1
+ { \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{q_stop} and \cs{q_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{q_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{q_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{q_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 ,
+ \q_mark \@@_reverse:wwNww ! ,
+ \q_mark \@@_reverse_end:ww
+ \q_stop ? \q_mark
+ }
+\cs_new:Npn \@@_reverse:wwNww
+ #1 , #2 \q_mark #3 #4 \q_stop ? #5 \q_mark
+ { #3 ? #2 \q_mark #3 #4 \q_stop #1 , #5 \q_mark }
+\cs_new:Npn \@@_reverse_end:ww #1 ! #2 , \q_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 |\q_mark \prg_return_false:| item.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \clist_if_empty:n #1 { p , T , F , TF }
+ {
+ \@@_if_empty_n:w ? #1
+ , \q_mark \prg_return_false:
+ , \q_mark \prg_return_true:
+ \q_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 \q_mark #2#3 \q_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:Npx \@@_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 to comma lists}
+%
+% \begin{macro}{\clist_map_function:NN, \clist_map_function:cN}
+% \UnitTested
+% \begin{macro}{\@@_map_function:Nw}
+% 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 one comma-delimited
+% item at a time. The end is marked by \cs{q_recursion_tail}.
+% The auxiliary function \cs{@@_map_function:Nw} is also used
+% in \cs{clist_map_inline:Nn}.
+% \begin{macrocode}
+\cs_new:Npn \clist_map_function:NN #1#2
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_last_unbraced:NNo \@@_map_function:Nw #2 #1
+ , \q_recursion_tail ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new:Npn \@@_map_function:Nw #1#2 ,
+ {
+ \quark_if_recursion_tail_break:nN {#2} \clist_map_break:
+ #1 {#2}
+ \@@_map_function:Nw #1
+ }
+\cs_generate_variant:Nn \clist_map_function:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_map_function:nN}
+% \UnitTested
+% \begin{macro}{\@@_map_function_n:Nn}
+% \begin{macro}{\@@_map_unbrace:Nw}
+% 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:Nw}.
+% \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 , \q_recursion_tail ,
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+\cs_new:Npn \@@_map_function_n:Nn #1 #2
+ {
+ \quark_if_recursion_tail_break:nN {#2} \clist_map_break:
+ \@@_map_unbrace:Nw #1 #2,
+ \exp_after:wN \@@_map_function_n:Nn \exp_after:wN #1
+ \exp:w \@@_trim_next:w \prg_do_nothing:
+ }
+\cs_new:Npn \@@_map_unbrace:Nw #1 #2, { #1 {#2} }
+% \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 , \q_recursion_tail ,
+ \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:Nnw}
+% As for other comma-list mappings, filter out the case of
+% an empty list. Same approach as \cs{clist_map_function:Nn},
+% additionally we store each item in the given variable.
+% As for inline mappings, space trimming for the \texttt{n}
+% variant is done by storing the comma list in a variable.
+% The quark test is done before assigning the item to the variable:
+% this avoids storing a quark which the user wouldn't expect.
+% The strange \cs{use:n} avoids unlikely problems when |#2| would
+% contain \cs{q_recursion_stop}.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_map_variable:NNn #1#2#3
+ {
+ \clist_if_empty:NF #1
+ {
+ \exp_args:Nno \use:nn
+ { \@@_map_variable:Nnw #2 {#3} }
+ #1
+ , \q_recursion_tail , \q_recursion_stop
+ \prg_break_point:Nn \clist_map_break: { }
+ }
+ }
+\cs_new_protected:Npn \clist_map_variable:nNn #1
+ {
+ \clist_set:Nn \l_@@_internal_clist {#1}
+ \clist_map_variable:NNn \l_@@_internal_clist
+ }
+\cs_new_protected:Npn \@@_map_variable:Nnw #1#2#3,
+ {
+ \quark_if_recursion_tail_stop:n {#3}
+ \tl_set:Nn #1 {#3}
+ \use:n {#2}
+ \@@_map_variable:Nnw #1 {#2}
+ }
+\cs_generate_variant:Nn \clist_map_variable:NNn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \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}
+% \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:Npx \clist_count:n #1
+ {
+ \exp_not:N \int_eval:n
+ {
+ 0
+ \exp_not:N \@@_count:w \c_space_tl
+ #1 \exp_not:n { , \q_recursion_tail , \q_recursion_stop }
+ }
+ }
+\cs_new:Npn \@@_count:n #1 { + 1 }
+\cs_new:Npx \@@_count:w #1 ,
+ {
+ \exp_not:n { \exp_args:Nf \quark_if_recursion_tail_stop:n } {#1}
+ \exp_not:N \tl_if_blank:nF {#1} { + 1 }
+ \exp_not:N \@@_count:w \c_space_tl
+ }
+% \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{q_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{q_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{q_mark} is taken as a third item, and now
+% the second \cs{q_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 ,
+ \q_mark , { \@@_use:nwwwwnwn {#3} }
+ \q_mark , { \@@_use:nwwn {#4} }
+ \q_stop { }
+ }
+ }
+ {
+ \__kernel_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 \q_mark , #6#7 \q_stop #8
+ { #6 {#3} , {#4} , #5 \q_mark , {#6} #7 \q_stop { #8 #1 #2 } }
+\cs_new:Npn \@@_use:nwwn #1#2 , #3 \q_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}
+%
+% \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_q_stop:w }
+ { \exp_args:Nf #4 { \int_eval:n { #2 + 1 + #1 } } }
+ }
+ {
+ \int_compare:nNnTF {#2} > {#1}
+ { \use_none_delimit_by_q_stop:w }
+ { #4 {#2} }
+ }
+ { } , #3 , \q_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_q_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}
+% \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_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 \q_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_defined:NT} and
+% \cs{msg_show:nnnnnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_show:N { \@@_show:NN \msg_show:nnxxxx }
+\cs_generate_variant:Nn \clist_show:N { c }
+\cs_new_protected:Npn \clist_log:N { \@@_show:NN \msg_log:nnxxxx }
+\cs_generate_variant:Nn \clist_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { LaTeX/kernel } { show-clist }
+ { \token_to_str:N #2 }
+ { \clist_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ }
+% \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:nnxxxx }
+\cs_new_protected:Npn \clist_log:n { \@@_show:Nn \msg_log:nnxxxx }
+\cs_new_protected:Npn \@@_show:Nn #1#2
+ {
+ #1 { LaTeX/kernel } { show-clist }
+ { } { \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3coffins.dtx b/macros/latex/contrib/l3kernel/l3coffins.dtx
new file mode 100644
index 0000000000..fa54e31825
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3coffins.dtx
@@ -0,0 +1,2526 @@
+% \iffalse meta-comment
+%
+%% File: l3coffins.dtx
+%
+% Copyright (C) 2010-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3coffins} package\\ Coffin code layer^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{box}
+% \cs{coffin_if_exist:NTF} \meta{box} \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 = 2019-01-21]
+% {
+% \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 = 2019-01-21]
+% {
+% \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
+% bottom edge of the bounding box 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}
+%
+% \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{dimension expression}.
+% \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{dimension expression}.
+% \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{dimension expression}.
+% \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}
+%
+% \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}
+%<*initex|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 }
+ {
+ \__kernel_msg_error:nnx { kernel } { unknown-coffin }
+ { \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
+ }
+ \@@_update: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
+ }
+ \@@_gupdate: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:NnnNN}
+% 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:NnnNN #1 {#2} {#3}
+ \vbox_set:Nn \@@_update:N
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnn { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnn #1#2#3
+ {
+ \@@_set_vertical:NnnNN #1 {#2} {#3}
+ \vbox_gset:Nn \@@_gupdate:N
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnn { c }
+\cs_new_protected:Npn \@@_set_vertical:NnnNN #1#2#3#4#5
+ {
+ \@@_if_exist:NT #1
+ {
+ #4 #1
+ {
+ \dim_set:Nn \tex_hsize:D {#2}
+%<*package>
+ \dim_set_eq:NN \linewidth \tex_hsize:D
+ \dim_set_eq:NN \columnwidth \tex_hsize:D
+%</package>
+ #3
+ }
+ #5 #1
+ \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 }
+ \@@_set_pole:Nnx #1 { T }
+ {
+ { 0pt }
+ {
+ \dim_eval:n
+ { \box_ht:N #1 - \box_ht:N \l_@@_internal_box }
+ }
+ { 1000pt }
+ { 0pt }
+ }
+ \box_clear:N \l_@@_internal_box
+ }
+ }
+% \end{macrocode}
+% \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:
+ \@@_update: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:
+ \@@_gupdate: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:NnNNNNw}
+% \begin{macro}{\vcoffin_set_end:, \vcoffin_gset_end:}
+% The same for vertical coffins.
+% \begin{macrocode}
+\cs_new_protected:Npn \vcoffin_set:Nnw #1#2
+ {
+ \@@_set_vertical:NnNNNNw #1 {#2} \vbox_set:Nw
+ \vcoffin_set_end:
+ \vbox_set_end: \@@_update:N
+ }
+\cs_generate_variant:Nn \vcoffin_set:Nnw { c }
+\cs_new_protected:Npn \vcoffin_gset:Nnw #1#2
+ {
+ \@@_set_vertical:NnNNNNw #1 {#2} \vbox_gset:Nw
+ \vcoffin_gset_end:
+ \vbox_gset_end: \@@_gupdate:N
+ }
+\cs_generate_variant:Nn \vcoffin_gset:Nnw { c }
+\cs_new_protected:Npn \@@_set_vertical:NnNNNNw #1#2#3#4#5#6
+ {
+ \@@_if_exist:NT #1
+ {
+ #3 #1
+ \dim_set:Nn \tex_hsize:D {#2}
+%<*package>
+ \dim_set_eq:NN \linewidth \tex_hsize:D
+ \dim_set_eq:NN \columnwidth \tex_hsize:D
+%</package>
+ \cs_set_protected:Npn #4
+ {
+ #5
+ #6 #1
+ \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 }
+ \@@_set_pole:Nnx #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_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
+ {
+ \__kernel_msg_error:nnxx { kernel } { unknown-coffin-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, \@@_set_pole:Nnx}
+% 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:cnx }
+\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:cnx }
+\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:cnx }
+\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:cnx }
+ \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:cnn { coffin ~ \@@_to_value:N #1 ~ poles }
+ {#2} {#3}
+ }
+\cs_generate_variant:Nn \@@_set_pole:Nnn { Nnx }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_update:N, \@@_gupdate:N}
+% Simple shortcuts.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_update:N #1
+ {
+ \@@_reset_structure:N #1
+ \@@_update_corners:N #1
+ \@@_update_poles:N #1
+ }
+\cs_new_protected:Npn \@@_gupdate: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:Nnx }
+\cs_new_protected:Npn \@@_gupdate_corners:N #1
+ { \@@_update_corners:NN #1 \prop_gput:Nnx }
+\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:Nnx }
+\cs_new_protected:Npn \@@_gupdate_poles:N #1
+ { \@@_update_poles:NN #1 \prop_gput:Nnx }
+\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
+ {
+ \__kernel_msg_error:nn { kernel } { 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:x
+ {
+ \@@_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_set_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
+ {
+ \tex_kern:D
+ \dim_eval:n
+ { \l_@@_bounding_shift_dim - \l_@@_left_corner_dim }
+ \exp_stop_f:
+ \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:Nnx \l_@@_bounding_prop { tl }
+ { { 0pt } { \dim_eval:n { \box_ht:N #1 } } }
+ \prop_put:Nnx \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:Nnx \l_@@_bounding_prop { bl }
+ { { 0pt } { \dim_use:N \l_@@_internal_dim } }
+ \prop_put:Nnx \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:Nnx \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:Nnx \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:Nnx \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:Nnx \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:Nnx \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}{\coffin_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:Nnx \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:Nnx \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:Nnx \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:Nnx \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
+ { \tex_kern:D -\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
+ { \tex_kern:D -\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
+ \coffin_set_eq:NN #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
+ \tex_kern:D -\box_wd:N #1
+ \tex_kern:D \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 to 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:Nnx \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:cnx
+ {
+ 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:Nnx #9 { T }
+ { { 0pt } {#6} { 1000pt } { 0pt } }
+ }
+ {
+ \@@_set_pole:Nnx #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:Nnx #9 { B }
+ { { 0pt } {#2} { 1000pt } { 0pt } }
+ }
+ {
+ \@@_set_pole:Nnx #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
+%<*initex>
+\tl_set:Nn \l_@@_display_font_tl { } % TODO
+%</initex>
+%<*package>
+\tl_set:Nn \l_@@_display_font_tl { \sffamily \tiny }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_color:n}
+% Calls \tn{color}, and otherwise does nothing if \tn{color} is not defined.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_color:n #1
+ { \cs_if_exist:NT \color { \color {#1} } }
+% \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
+ {
+%<*initex>
+ \hbox:n { \tex_vrule:D width 1pt height 1pt \scan_stop: } % TODO
+%</initex>
+%<*package>
+ \@@_color:n {#4}
+ \rule { 1pt } { 1pt }
+%</package>
+ }
+ \@@_attach_mark:NnnNnnnn #1 {#2} {#3}
+ \l_@@_display_pole_coffin { hc } { vc } { 0pt } { 0pt }
+ \hcoffin_set:Nn \l_@@_display_coord_coffin
+ {
+%<*initex>
+ % TODO
+%</initex>
+%<*package>
+ \@@_color:n {#4}
+%</package>
+ \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
+ {
+%<*initex>
+ \hbox:n { \tex_vrule:D width 1pt height 1pt \scan_stop: } % TODO
+%</initex>
+%<*package>
+ \@@_color:n {#2}
+ \rule { 1pt } { 1pt }
+%</package>
+ }
+ \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
+ {
+%<*initex>
+ % TODO
+%</initex>
+%<*package>
+ \@@_color:n {#6}
+%</package>
+ \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
+ \tex_kern:D -\box_wd:N \l_@@_display_coffin
+ \tex_kern:D \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:nnxxxx }
+\cs_generate_variant:Nn \coffin_show_structure:N { c }
+\cs_new_protected:Npn \coffin_log_structure:N
+ { \@@_show_structure:NN \msg_log:nnxxxx }
+\cs_generate_variant:Nn \coffin_log_structure:N { c }
+\cs_new_protected:Npn \@@_show_structure:NN #1#2
+ {
+ \@@_if_exist:NT #2
+ {
+ #1 { LaTeX / kernel } { show-coffin }
+ { \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}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { unknown-coffin }
+ { Unknown~coffin~'#1'. }
+ { The~coffin~'#1'~was~never~defined. }
+\__kernel_msg_new:nnnn { kernel } { unknown-coffin-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.
+ }
+\__kernel_msg_new:nnn { kernel } { show-coffin }
+ {
+ Size~of~coffin~#1 : #2 \\
+ Poles~of~coffin~#1 : #3 .
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3color-base.dtx b/macros/latex/contrib/l3kernel/l3color-base.dtx
new file mode 100644
index 0000000000..4881aae53e
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3color-base.dtx
@@ -0,0 +1,192 @@
+% \iffalse meta-comment
+%
+%% File: l3color-base.dtx
+%
+% Copyright (C) 2011,2012,2014,2016-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3color-base} package\\ Color support^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides support for color in \LaTeX3{}. At present, the
+% material here is mainly intended to support a small number of low-level
+% requirements in other \pkg{l3kernel} modules.
+%
+% \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.
+% \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}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3color-base} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=color>
+% \end{macrocode}
+%
+% \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} is space-separated as
+% this allows it to be used directly in specials in many common cases.
+% This internal representation is close to that used by the \texttt{dvips}
+% program.
+% \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:
+ {
+%<*package>
+ \@@_backend_pickup:N \l_@@_current_tl
+%</package>
+ \@@_select:V \l_@@_current_tl
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_select:n, \@@_select:V}
+% \begin{macro}{\@@_select:w}
+% \begin{macro}
+% {\@@_select_cmyk:w, \@@_select_gray:w, \@@_select_rgb:w, \@@_select_spot:w}
+% 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 experimental material.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_select:n #1
+ { \@@_select:w #1 \q_stop }
+\cs_generate_variant:Nn \@@_select:n { V }
+\cs_new_protected:Npn \@@_select:w #1 ~ #2 \q_stop
+ { \use:c { @@_select_ #1 :w } #2 \q_stop }
+\cs_new_protected:Npn \@@_select_cmyk:w #1 ~ #2 ~ #3 ~ #4 \q_stop
+ { \@@_backend_cmyk:nnnn {#1} {#2} {#3} {#4} }
+\cs_new_protected:Npn \@@_select_gray:w #1 \q_stop
+ { \@@_backend_gray:n {#1} }
+\cs_new_protected:Npn \@@_select_rgb:w #1 ~ #2 ~ #3 \q_stop
+ { \@@_backend_rgb:nnn {#1} {#2} {#3} }
+\cs_new_protected:Npn \@@_select_spot:w #1 ~ #2 \q_stop
+ { \@@_backend_spot:nn {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_current_tl}
+% As the setting data is used only
+% for specials, and those are always space-separated, it makes most sense to
+% hold the internal information in that form.
+% \begin{macrocode}
+\tl_new:N \l_@@_current_tl
+\tl_set:Nn \l_@@_current_tl { gray~0 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3deprecation.dtx b/macros/latex/contrib/l3kernel/l3deprecation.dtx
new file mode 100644
index 0000000000..50219502b0
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3deprecation.dtx
@@ -0,0 +1,945 @@
+% \iffalse meta-comment
+%
+%% File: l3deprecation.dtx
+%
+% Copyright (C) 2017-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Deprecation errors^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3deprecation} documentation}
+%
+% A few commands have had to be deprecated over the years. This module
+% defines deprecated and deleted commands to produce an error.
+%
+% The life of a deprecated command has several stages.
+% \begin{itemize}
+% \item When we decide it should be eventually removed the command's
+% definition is put in this file \pkg{l3deprecation.dtx} and the name
+% of the command is added to the list in \pkg{l3obsolete.txt}. The
+% command remains defined by default for $12$ or more months,
+% typically until the end of a year. During that time, if \pkg{expl3}
+% is loaded with any of the options \texttt{check-declarations} or
+% \texttt{log-functions} or \texttt{enable-debug} typically used in
+% package test files, the command will produce a warning.
+% \item Then by default the command produces an error for all users,
+% which can be suppressed for $6$ months through the option
+% \texttt{undo-recent-deprecations}.
+% \item After this $6$-month grace period, the command irreversibly
+% produces an error. Its original definition can then be removed from
+% the sources, leaving only the error definition. In
+% \pkg{l3obsolete.txt} its name is moved from the \enquote{Deprecated
+% functions and variables} list to the \enquote{Removed functions and
+% variables} list.
+% \end{itemize}
+% Package authors are encouraged to have a test file with
+% \cs{debug_on:n} |{deprecation}|, which makes commands at all stages of
+% this list into errors. This helps detect uses of deprecated commands
+% before user complain.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3deprecation} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=deprecation>
+% \end{macrocode}
+%
+% \subsection{Helpers and variables}
+%
+% \begin{variable}{\l_@@_grace_period_bool}
+% This is set to \texttt{true} when the deprecated command that is
+% being defined is in its grace period, meaning between the time it
+% becomes an error by default and the time $6$~months later where even
+% \texttt{undo-recent-deprecations} stops restoring it.
+% \begin{macrocode}
+\bool_new:N \l_@@_grace_period_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_date_compare:nNnTF, \@@_date_compare_aux:w}
+% Expects |#1| and |#3| to be dates in the format YYYY-MM-DD (but
+% accepts YYYY or YYYY-MM too, filling in zeros for the missing data).
+% Compares them using |#2| (one of |<|, |=|, |>|).
+% \begin{macrocode}
+\cs_new:Npn \@@_date_compare:nNnTF #1#2#3
+ { \@@_date_compare_aux:w #1 -0-0- \q_mark #2 #3 -0-0- \q_stop }
+\cs_new:Npn \@@_date_compare_aux:w
+ #1 - #2 - #3 - #4 \q_mark #5 #6 - #7 - #8 - #9 \q_stop
+ {
+ \int_compare:nNnTF {#1} = {#6}
+ {
+ \int_compare:nNnTF {#2} = {#7}
+ { \int_compare:nNnTF {#3} #5 {#8} }
+ { \int_compare:nNnTF {#2} #5 {#7} }
+ }
+ { \int_compare:nNnTF {#1} #5 {#6} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_not_yet_deprecated:nTF, \@@_minus_six_months:w}
+% Receives a deprecation \meta{date} and runs the \texttt{true}
+% (\texttt{false}) branch if the \pkg{expl3} date is earlier (later)
+% than \meta{date}. If \texttt{undo-recent-deprecations} is used we
+% subtract $6$ months to the \pkg{expl3} date (equivalently add $6$
+% months to the \meta{date}). In addition, if the \pkg{expl3} date is
+% between \meta{date} and \meta{date} plus $6$ months,
+% \cs{l_@@_grace_period_bool} is set to \texttt{true}, otherwise
+% \texttt{false}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_not_yet_deprecated:nTF #1
+ {
+ \bool_set_false:N \l_@@_grace_period_bool
+ \exp_args:No \@@_date_compare:nNnTF { \ExplLoaderFileDate } < {#1}
+ { \use_i:nn }
+ {
+ \exp_args:Nf \@@_date_compare:nNnTF
+ {
+ \exp_after:wN \@@_minus_six_months:w
+ \ExplLoaderFileDate -0-0- \q_stop
+ } < {#1}
+ {
+ \bool_set_true:N \l_@@_grace_period_bool
+ \bool_if:NTF \l@expl@undo@recent@deprecations@bool
+ }
+ { \use_ii:nn }
+ }
+ }
+\cs_new:Npn \@@_minus_six_months:w #1 - #2 - #3 - #4 \q_stop
+ {
+ \int_compare:nNnTF {#2} > 6
+ { #1 - \int_eval:n { #2 - 6 } - #3 }
+ { \int_eval:n { #1 - 1 } - \int_eval:n { #2 + 6 } - #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \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 a warning and run its
+% \meta{code}, or to produce an error and not run any \meta{code},
+% depending on the \pkg{expl3} date.
+% \begin{itemize}
+% \item If the \pkg{expl3} date is less than the \meta{date} (plus
+% $6$~months in case \texttt{undo-recent-deprecations} is used) then
+% we define the \meta{function} to produce a warning and run its code.
+% The warning is actually suppressed in two cases:
+% \begin{itemize}
+% \item if neither \texttt{undo-recent-deprecations} nor
+% \texttt{enable-debug} are in effect we may be in an end-user's
+% document so it is suppressed;
+% \item if the command is expandable then we cannot produce a warning.
+% \end{itemize}
+% \item Otherwise, we define the \meta{function} to produce an error.
+% \end{itemize}
+% In both cases we additionally 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 later sections we use the \pkg{l3doc} key \texttt{deprecated} with
+% a date equal to that \meta{date} plus $6$~months, so that \pkg{l3doc}
+% will complain if we forget to remove the stale \meta{parameters} and
+% \Arg{code}.
+%
+% 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.
+%
+% Then check the date (taking into account
+% \texttt{undo-recent-deprecations}) to see if the command should be
+% deprecated right away (\texttt{false} branch of
+% \cs{@@_not_yet_deprecated:nTF}), in which case
+% \cs{@@_just_error:nnNN} makes \meta{function} into an error (not
+% \tn{outer}), ignoring its \meta{parameters} and \meta{code}
+% completely.
+%
+% Otherwise distinguish cases where we should give a warning from
+% those where we shouldn't: warnings can only happen for protected
+% commands, and we only want them if either
+% \texttt{undo-recent-deprecations} or \texttt{enable-debug} is in
+% force, not for standard users.
+% \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: }
+ \@@_not_yet_deprecated:nTF {#1}
+ {
+ \bool_if:nTF
+ {
+ \cs_if_eq_p:NN #3 \cs_new_protected:Npn &&
+ \__kernel_if_debug:TF
+ { \c_true_bool } { \l@expl@undo@recent@deprecations@bool }
+ }
+ { \@@_warn_once:nnNnn {#1} {#2} #4 {#5} {#6} }
+ { \@@_patch_aux:Nn #3 { #4 #5 {#6} } }
+ }
+ { \@@_just_error:nnNN {#1} {#2} #3 #4 }
+ }
+% \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{x}-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_new_protected:Npx #3
+ {
+ \__kernel_if_debug:TF
+ {
+ \exp_not:N \__kernel_msg_warning:nnxxx
+ { kernel } { 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_new_protected:Npn
+ { \__kernel_deprecation_code:nn { } { \cs_set_protected:Npn #2 } }
+ { \__kernel_deprecation_code:nn { } { \cs_set:Npn #2 } }
+ }
+% \end{macrocode}
+% Finally, if we want an error we reuse the same \cs{@@_patch_aux:Nn}
+% as the previous case. Indeed, we want \cs{debug_off:n}
+% |{deprecation}| to make the \meta{function} into an error, just like
+% it is by default. The error is expandable or not, and the last
+% argument of the error message is empty or is \texttt{grace} to
+% denote the case where we are in the $6$~month grace period, in which
+% case the error message is more detailed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_just_error:nnNN #1#2#3#4
+ {
+ \exp_args:NNx \@@_patch_aux:Nn #3
+ {
+ \exp_not:N #4
+ {
+ \cs_if_eq:NNTF #3 \cs_new_protected:Npn
+ { \exp_not:N \__kernel_msg_error:nnnnnn }
+ { \exp_not:N \__kernel_msg_expandable_error:nnnnnn }
+ { kernel } { deprecated-command }
+ {#1}
+ { \token_to_str:N #4 }
+ { \tl_to_str:n {#2} }
+ { \bool_if:NT \l_@@_grace_period_bool { grace } }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\__kernel_deprecation_error:Nnn}
+% The \tn{outer} definition here ensures the command cannot appear
+% in an argument. Use this auxiliary on all commands that have been
+% removed since 2015.
+% \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 \__kernel_msg_expandable_error:nnnnn
+ { kernel } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ \exp_not:N \__kernel_msg_error:nnxxx
+ { kernel } { deprecated-command }
+ { \tl_to_str:n {#3} } { \token_to_str:N #1 } { \tl_to_str:n {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { deprecated-command }
+ {
+ '#2'~deprecated~on~#1.
+ \tl_if_empty:nF {#3} { ~Use~'#3'. }
+ \str_if_eq:nnT {#4} { grace }
+ {
+ \c_space_tl
+ For~6~months~after~that~date~one~can~restore~a~deprecated~
+ command~by~loading~the~expl3~package~with~the~option~
+ 'undo-recent-deprecations'.
+ }
+ }
+% \end{macrocode}
+%
+% \subsection{Removed functions}
+%
+% \begin{macro}{\@@_old_protected:Nnn, \@@_old:Nnn}
+% Short-hands for old commands whose definition does not matter
+% anymore, i.e., commands past the grace period.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_old_protected:Nnn #1#2#3
+ {
+ \__kernel_patch_deprecation:nnNNpn {#3} {#2}
+ \cs_new_protected:Npn #1 { }
+ }
+\cs_new_protected:Npn \@@_old:Nnn #1#2#3
+ {
+ \__kernel_patch_deprecation:nnNNpn {#3} {#2}
+ \cs_new:Npn #1 { }
+ }
+\@@_old:Nnn \box_resize:Nnn
+ { \box_resize_to_wd_and_ht_plus_dp:Nnn } { 2019-01-01 }
+\@@_old:Nnn \box_use_clear:N
+ { \box_use_drop:N } { 2019-01-01 }
+\@@_old:Nnn \c_job_name_tl
+ { \c_sys_jobname_str } { 2017-01-01 }
+\@@_old:Nnn \c_minus_one
+ { -1 } { 2019-01-01 }
+\@@_old:Nnn \dim_case:nnn
+ { \dim_case:nnF } { 2015-07-14 }
+\@@_old:Nnn \file_add_path:nN
+ { \file_get_full_name:nN } { 2019-01-01 }
+\@@_old_protected:Nnn \file_if_exist_input:nT
+ { \file_if_exist:nT and~ \file_input:n } { 2018-03-05 }
+\@@_old_protected:Nnn \file_if_exist_input:nTF
+ { \file_if_exist:nT and~ \file_input:n } { 2018-03-05 }
+\@@_old:Nnn \file_list:
+ { \file_log_list: } { 2019-01-01 }
+\@@_old:Nnn \file_path_include:n
+ { \seq_put_right:Nn \l_file_search_path_seq } { 2019-01-01 }
+\@@_old:Nnn \file_path_remove:n
+ { \seq_remove_all:Nn \l_file_search_path_seq } { 2019-01-01 }
+\@@_old:Nnn \g_file_current_name_tl
+ { \g_file_curr_name_str } { 2019-01-01 }
+\@@_old:Nnn \int_case:nnn
+ { \int_case:nnF } { 2015-07-14 }
+\@@_old:Nnn \int_from_binary:n
+ { \int_from_bin:n } { 2016-01-05 }
+\@@_old:Nnn \int_from_hexadecimal:n
+ { \int_from_hex:n } { 2016-01-05 }
+\@@_old:Nnn \int_from_octal:n
+ { \int_from_oct:n } { 2016-01-05 }
+\@@_old:Nnn \int_to_binary:n
+ { \int_to_bin:n } { 2016-01-05 }
+\@@_old:Nnn \int_to_hexadecimal:n
+ { \int_to_hex:n } { 2016-01-05 }
+\@@_old:Nnn \int_to_octal:n
+ { \int_to_oct:n } { 2016-01-05 }
+\@@_old_protected:Nnn \ior_get_str:NN
+ { \ior_str_get:NN } { 2018-03-05 }
+\@@_old:Nnn \ior_list_streams:
+ { \ior_show_list: } { 2019-01-01 }
+\@@_old:Nnn \ior_log_streams:
+ { \ior_log_list: } { 2019-01-01 }
+\@@_old:Nnn \iow_list_streams:
+ { \iow_show_list: } { 2019-01-01 }
+\@@_old:Nnn \iow_log_streams:
+ { \iow_log_list: } { 2019-01-01 }
+\@@_old:Nnn \luatex_if_engine_p:
+ { \sys_if_engine_luatex_p: } { 2017-01-01 }
+\@@_old:Nnn \luatex_if_engine:F
+ { \sys_if_engine_luatex:F } { 2017-01-01 }
+\@@_old:Nnn \luatex_if_engine:T
+ { \sys_if_engine_luatex:T } { 2017-01-01 }
+\@@_old:Nnn \luatex_if_engine:TF
+ { \sys_if_engine_luatex:TF } { 2017-01-01 }
+\@@_old:Nnn \pdftex_if_engine_p:
+ { \sys_if_engine_pdftex_p: } { 2017-01-01 }
+\@@_old:Nnn \pdftex_if_engine:F
+ { \sys_if_engine_pdftex:F } { 2017-01-01 }
+\@@_old:Nnn \pdftex_if_engine:T
+ { \sys_if_engine_pdftex:T } { 2017-01-01 }
+\@@_old:Nnn \pdftex_if_engine:TF
+ { \sys_if_engine_pdftex:TF } { 2017-01-01 }
+\@@_old:Nnn \prop_get:cn
+ { \prop_item:cn } { 2016-01-05 }
+\@@_old:Nnn \prop_get:Nn
+ { \prop_item:Nn } { 2016-01-05 }
+\@@_old:Nnn \quark_if_recursion_tail_break:N
+ { } { 2015-07-14 }
+\@@_old:Nnn \quark_if_recursion_tail_break:n
+ { } { 2015-07-14 }
+\@@_old:Nnn \scan_align_safe_stop:
+ { protected~commands } { 2017-01-01 }
+\@@_old:Nnn \sort_ordered:
+ { \sort_return_same: } { 2019-01-01 }
+\@@_old:Nnn \sort_reversed:
+ { \sort_return_swapped: } { 2019-01-01 }
+\@@_old:Nnn \str_case:nnn
+ { \str_case:nnF } { 2015-07-14 }
+\@@_old:Nnn \str_case:onn
+ { \str_case:onF } { 2015-07-14 }
+\@@_old:Nnn \str_case_x:nnn
+ { \str_case_e:nnF } { 2015-07-14 }
+\@@_old:Nnn \tl_case:cnn
+ { \tl_case:cnF } { 2015-07-14 }
+\@@_old:Nnn \tl_case:Nnn
+ { \tl_case:NnF } { 2015-07-14 }
+\@@_old_protected:Nnn \tl_to_lowercase:n
+ { \tex_lowercase:D } { 2018-03-05 }
+\@@_old_protected:Nnn \tl_to_uppercase:n
+ { \tex_uppercase:D } { 2018-03-05 }
+\@@_old:Nnn \token_new:Nn
+ { \cs_new_eq:NN } { 2019-01-01 }
+\@@_old:Nnn \xetex_if_engine_p:
+ { \sys_if_engine_xetex_p: } { 2017-01-01 }
+\@@_old:Nnn \xetex_if_engine:F
+ { \sys_if_engine_xetex:F } { 2017-01-01 }
+\@@_old:Nnn \xetex_if_engine:T
+ { \sys_if_engine_xetex:T } { 2017-01-01 }
+\@@_old:Nnn \xetex_if_engine:TF
+ { \sys_if_engine_xetex:TF } { 2017-01-01 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated primitives}
+%
+% \begin{macro}[deprecated = 2020-07-01]{\etex_beginL:D}
+% \begin{macro}{\@@_primitive:NN, \@@_primitive:w}
+% We renamed all primitives to \cs[no-index]{tex_\ldots{}:D} so all
+% others are deprecated. In \pkg{l3names}, \cs{__kernel_primitives:}
+% is defined to contain \cs{__kernel_primitive:NN} \cs{beginL}
+% \cs{etex_beginL:D} and so on, one for each deprecated primitive. We
+% apply \cs{exp_not:N} to the second argument of
+% \cs{__kernel_primitive:NN} because it may be outer (both when doing
+% and undoing deprecation actually), then \cs{@@_primitive:NN} uses
+% \cs{tex_let:D} to change the meaning of this potentially outer
+% token. Then, either turn it into an error or make it equal to the
+% primitive~|#1|. To be more precise, |#1| may not be defined, so try
+% a \cs[no-index]{tex_\ldots{}:D} command as well.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_primitive:NN #1#2 { }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_primitive:w #1 { \token_to_str:N _ } { }
+\__kernel_deprecation_code:nn
+ {
+ \cs_set_protected:Npn \__kernel_primitive:NN #1
+ {
+ \exp_after:wN \@@_primitive:NN
+ \exp_after:wN #1
+ \exp_not:N
+ }
+ \cs_set_protected:Npn \@@_primitive:NN #1#2
+ {
+ \tex_let:D #2 \scan_stop:
+ \exp_args:NNx \__kernel_deprecation_error:Nnn #2
+ {
+ \iow_char:N \\
+ \cs_if_exist:NTF #1
+ { \cs_to_str:N #1 }
+ {
+ tex_
+ \exp_last_unbraced:Nf
+ \@@_primitive:w { \cs_to_str:N #2 }
+ }
+ }
+ { 2020-01-01 }
+ }
+ \__kernel_primitives:
+ }
+ {
+ \cs_set_protected:Npn \__kernel_primitive:NN #1
+ {
+ \exp_after:wN \@@_primitive:NN
+ \exp_after:wN #1
+ \exp_not:N
+ }
+ \cs_set_protected:Npn \@@_primitive:NN #1#2
+ {
+ \tex_let:D #2 #1
+ \cs_if_exist:cT { tex_ \cs_to_str:N #1 :D }
+ { \cs_set_eq:Nc #2 { tex_ \cs_to_str:N #1 :D } }
+ }
+ \__kernel_primitives:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3box} functions}
+%
+% \begin{macro}[deprecated = 2021-07-01]
+% {
+% \box_set_eq_clear:NN, \box_set_eq_clear:cN,
+% \box_set_eq_clear:Nc, \box_set_eq_clear:cc
+% }
+% \begin{macro}[deprecated = 2021-07-01]
+% {
+% \box_gset_eq_clear:NN, \box_gset_eq_clear:cN,
+% \box_gset_eq_clear:Nc, \box_gset_eq_clear:cc
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \box_set_eq_drop:N }
+\cs_new_protected:Npn \box_set_eq_clear:NN #1#2
+ { \tex_setbox:D #1 \tex_box:D #2 }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \box_gset_eq_drop:N }
+\cs_new_protected:Npn \box_gset_eq_clear:NN #1#2
+ { \tex_global:D \tex_setbox:D #1 \tex_box:D #2 }
+\cs_generate_variant:Nn \box_set_eq_clear:NN { c , Nc , cc }
+\cs_generate_variant:Nn \box_gset_eq_clear:NN { c , Nc , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[deprecated = 2021-07-01]{\hbox_unpack_clear:N, \hbox_unpack_clear:c}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \hbox_unpack_drop:N }
+\cs_new_protected:Npn \hbox_unpack_clear:N
+ { \hbox_unpack_drop:N }
+\cs_generate_variant:Nn \hbox_unpack_clear:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated = 2021-07-01]{\vbox_unpack_clear:N, \vbox_unpack_clear:c}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \vbox_unpack_drop:N }
+\cs_new_protected:Npn \vbox_unpack_clear:N
+ { \vbox_unpack_drop:N }
+\cs_generate_variant:Nn \vbox_unpack_clear:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3int} functions}
+%
+% \begin{macrocode}
+%<@@=int>
+% \end{macrocode}
+%
+% \begin{variable}[deprecated = 2020-07-01]
+% {
+% \c_zero, \c_one, \c_two, \c_three, \c_four, \c_five, \c_six,
+% \c_seven, \c_eight, \c_nine, \c_ten, \c_eleven, \c_twelve,
+% \c_thirteen, \c_fourteen, \c_fifteen, \c_sixteen, \c_thirty_two,
+% \c_one_hundred, \c_two_hundred_fifty_five,
+% \c_two_hundred_fifty_six, \c_one_thousand, \c_ten_thousand,
+% }
+% \begin{macro}{\@@_deprecated_constants:nn}
+% Constants that are now deprecated. By default define them with
+% \cs{int_const:Nn}. To deprecate them call for instance
+% \cs{__kernel_deprecation_error:Nnn} \cs{c_zero} |{0}|
+% |{2020-01-01}|. To redefine them (locally), use
+% \cs{@@_constdef:Nw}, with an \cs{exp_not:N} construction because the
+% constants themselves are outer at that point.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_deprecated_constants:nn #1#2
+ {
+ #1 \c_zero { 0 } #2
+ #1 \c_one { 1 } #2
+ #1 \c_two { 2 } #2
+ #1 \c_three { 3 } #2
+ #1 \c_four { 4 } #2
+ #1 \c_five { 5 } #2
+ #1 \c_six { 6 } #2
+ #1 \c_seven { 7 } #2
+ #1 \c_eight { 8 } #2
+ #1 \c_nine { 9 } #2
+ #1 \c_ten { 10 } #2
+ #1 \c_eleven { 11 } #2
+ #1 \c_twelve { 12 } #2
+ #1 \c_thirteen { 13 } #2
+ #1 \c_fourteen { 14 } #2
+ #1 \c_fifteen { 15 } #2
+ #1 \c_sixteen { 16 } #2
+ #1 \c_thirty_two { 32 } #2
+ #1 \c_one_hundred { 100 } #2
+ #1 \c_two_hundred_fifty_five { 255 } #2
+ #1 \c_two_hundred_fifty_six { 256 } #2
+ #1 \c_one_thousand { 1000 } #2
+ #1 \c_ten_thousand { 10000 } #2
+ }
+\@@_deprecated_constants:nn { \int_const:Nn } { }
+\__kernel_deprecation_code:nn
+ {
+ \@@_deprecated_constants:nn
+ { \exp_after:wN \__kernel_deprecation_error:Nnn \exp_not:N }
+ { { 2020-01-01 } }
+ }
+ {
+ \@@_deprecated_constants:nn
+ {
+ \exp_after:wN \use:nnn
+ \exp_after:wN \@@_constdef:Nw \exp_not:N
+ }
+ { \exp_stop_f: }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+%
+% \begin{macro}[deprecated = 2020-07-01]{\@@_value:w}
+% Made public.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_value:w \int_value:w
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3luatex} functions}
+%
+% \begin{macrocode}
+%<@@=lua>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, deprecated = 2020-07-01]{\lua_now_x:n, \lua_escape_x:n}
+% \begin{macro}[deprecated = 2020-07-01]{\lua_shipout_x:n}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \lua_now:e }
+\cs_new:Npn \lua_now_x:n #1 { \@@_now:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \lua_escape:e }
+\cs_new:Npn \lua_escape_x:n #1 { \@@_escape:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \lua_shipout_e:n }
+\cs_new_protected:Npn \lua_shipout_x:n #1 { \@@_shipout:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3msg} functions}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated = 2020-07-01]{\msg_log:n, \msg_term:n}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \iow_log:n }
+\cs_new_protected:Npn \msg_log:n #1
+ {
+ \iow_log:n { ................................................. }
+ \iow_wrap:nnnN { . ~ #1} { . ~ } { } \iow_log:n
+ \iow_log:n { ................................................. }
+ }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \iow_term:n }
+\cs_new_protected:Npn \msg_term:n #1
+ {
+ \iow_term:n { ************************************************* }
+ \iow_wrap:nnnN { * ~ #1} { * ~ } { } \iow_term:n
+ \iow_term:n { ************************************************* }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[deprecated = 2020-07-01]{\msg_interrupt:nnn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { [Defined~error~message] }
+\cs_new_protected:Npn \msg_interrupt:nnn #1#2#3
+ {
+ \tl_if_empty:nTF {#3}
+ {
+ \@@_old_interrupt_wrap:nn { \\ \c_@@_no_info_text_tl }
+ {#1 \\\\ #2 \\\\ \c_@@_continue_text_tl }
+ }
+ {
+ \@@_old_interrupt_wrap:nn { \\ #3 }
+ {#1 \\\\ #2 \\\\ \c_@@_help_text_tl }
+ }
+ }
+\cs_new_protected:Npn \@@_old_interrupt_wrap:nn #1#2
+ {
+ \iow_wrap:nnnN {#1} { | ~ } { } \@@_old_interrupt_more_text:n
+ \iow_wrap:nnnN {#2} { ! ~ } { } \@@_old_interrupt_text:n
+ }
+\cs_new_protected:Npn \@@_old_interrupt_more_text:n #1
+ {
+ \exp_args:Nx \tex_errhelp:D
+ {
+ |'''''''''''''''''''''''''''''''''''''''''''''''
+ #1 \iow_newline:
+ |...............................................
+ }
+ }
+\group_begin:
+ \char_set_lccode:nn {`\{} {`\ }
+ \char_set_lccode:nn {`\}} {`\ }
+ \char_set_lccode:nn {`\&} {`\!}
+ \char_set_catcode_active:N \&
+\tex_lowercase:D
+ {
+ \group_end:
+ \cs_new_protected:Npn \@@_old_interrupt_text:n #1
+ {
+ \iow_term:x
+ {
+ \iow_newline:
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ \iow_newline:
+ !
+ }
+ \__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{Deprecated \pkg{l3prg} functions}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated = 2020-07-01]
+% {
+% \@@_break_point:Nn,
+% \@@_break_point:,
+% \@@_map_break:Nn,
+% \@@_break:,
+% \@@_break:n
+% }
+% Made public, but used by a few third-parties. It's not possible to
+% perfectly support a mixture of \cs{@@_map_break:Nn} and
+% \cs{prg_map_break:Nn} because they use different delimiters. The
+% following code only breaks if someone tries to break from two
+% \enquote{old-style} \cs{@@_map_break:Nn} \ldots{}
+% \cs{@@_break_point:Nn} mappings in one go. Basically, the
+% \cs{@@_map_break:Nn} converts a single \cs{@@_break_point:Nn} to
+% \cs{prg_break_point:Nn}, and that delimiter had better be the right
+% one. Then we call \cs{prg_map_break:Nn} which may end up breaking
+% intermediate looks in the (unbraced) argument |#1|. It is essential
+% to define the |break_point| functions before the corresponding
+% |break| functions: otherwise \cs{debug_on:n} |{deprecation}|
+% \cs{debug_off:n} |{deprecation}| would break when trying to restore
+% the definitions because they would involve deprecated commands whose
+% definition has not yet been restored.
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \prg_break_point:Nn }
+\cs_new:Npn \@@_break_point:Nn { \prg_break_point:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \prg_break_point: }
+\cs_new:Npn \@@_break_point: { \prg_break_point: }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \prg_map_break:Nn }
+\cs_new:Npn \@@_map_break:Nn #1 \@@_break_point:Nn
+ { \prg_map_break:Nn #1 \prg_break_point:Nn }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \prg_break: }
+\cs_new:Npn \@@_break: #1 \@@_break_point: { }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \prg_break:n }
+\cs_new:Npn \@@_break:n #1#2 \@@_break_point: {#1}
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3str} functions}
+%
+% \begin{macro}[EXP, deprecated = 2020-07-01, noTF]{\str_case_x:nn}
+% \begin{macro}[EXP, deprecated = 2020-07-01, pTF]{\str_if_eq_x:nn}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_case_e:nn }
+\cs_new:Npn \str_case_x:nn { \str_case_e:nn }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_case_e:nnT }
+\cs_new:Npn \str_case_x:nnT { \str_case_e:nnT }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_case_e:nnF }
+\cs_new:Npn \str_case_x:nnF { \str_case_e:nnF }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_case_e:nnTF }
+\cs_new:Npn \str_case_x:nnTF { \str_case_e:nnTF }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_if_eq_p:ee }
+\cs_new:Npn \str_if_eq_x_p:nn { \str_if_eq_p:ee }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_if_eq:eeT }
+\cs_new:Npn \str_if_eq_x:nnT { \str_if_eq:eeT }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_if_eq:eeF }
+\cs_new:Npn \str_if_eq_x:nnF { \str_if_eq:eeF }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \str_if_eq:eeTF }
+\cs_new:Npn \str_if_eq_x:nnTF { \str_if_eq:eeTF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Deprecated \pkg{l3tl} functions}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \begin{macro}[deprecated = 2021-07-01]
+% {
+% \tl_set_from_file:Nnn, \tl_set_from_file:cnn,
+% \tl_gset_from_file:Nnn, \tl_gset_from_file:cnn,
+% \tl_set_from_file_x:Nnn, \tl_set_from_file_x:cnn,
+% \tl_gset_from_file_x:Nnn, \tl_gset_from_file_x:cnn
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \file_get:nnN }
+\cs_new_protected:Npn \tl_set_from_file:Nnn #1#2#3
+ { \file_get:nnN {#3} {#2} #1 }
+\cs_generate_variant:Nn \tl_set_from_file:Nnn { c }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \file_get:nnN }
+\cs_new_protected:Npn \tl_gset_from_file:Nnn #1#2#3
+ {
+ \group_begin:
+ \file_get:nnN {#3} {#2} \l_@@_internal_a_tl
+ \tl_gset_eq:NN #1 \l_@@_internal_a_tl
+ \group_end:
+ }
+\cs_generate_variant:Nn \tl_gset_from_file:Nnn { c }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \file_get:nnN }
+\cs_new_protected:Npn \tl_set_from_file_x:Nnn #1#2#3
+ {
+ \group_begin:
+ \file_get:nnN {#3} {#2} \l_@@_internal_a_tl
+ #2 \scan_stop:
+ \tl_set:Nx \l_@@_internal_a_tl { \l_@@_internal_a_tl }
+ \exp_args:NNNo \group_end:
+ \tl_set:Nn #1 \l_@@_internal_a_tl
+ }
+\cs_generate_variant:Nn \tl_set_from_file_x:Nnn { c }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \file_get:nnN }
+\cs_new_protected:Npn \tl_gset_from_file_x:Nnn #1#2#3
+ {
+ \group_begin:
+ \file_get:nnN {#3} {#2} \l_@@_internal_a_tl
+ #2 \scan_stop:
+ \tl_gset:Nx #1 { \l_@@_internal_a_tl }
+ \group_end:
+ }
+\cs_generate_variant:Nn \tl_gset_from_file_x:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3tl-analysis} functions}
+%
+% \begin{macro}[deprecated = 2020-07-01]
+% {\tl_show_analysis:N, \tl_show_analysis:n}
+% Simple renames.
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \tl_analysis_show:N }
+\cs_new_protected:Npn \tl_show_analysis:N { \tl_analysis_show:N }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-01 } { \tl_analysis_show:n }
+\cs_new_protected:Npn \tl_show_analysis:n { \tl_analysis_show:n }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3token} functions}
+%
+% \begin{macro}[EXP, deprecated = 2021-07-01]
+% {
+% \token_get_prefix_spec:N,
+% \token_get_arg_spec:N,
+% \token_get_replacement_spec:N
+% }
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \cs_prefix_spec:N }
+\cs_new:Npn \token_get_prefix_spec:N { \cs_prefix_spec:N }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \cs_argument_spec:N }
+\cs_new:Npn \token_get_arg_spec:N { \cs_argument_spec:N }
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { \cs_replacement_spec:N }
+\cs_new:Npn \token_get_replacement_spec:N { \cs_replacement_spec:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Deprecated \pkg{l3file} functions}
+%
+% \begin{macro}[EXP, deprecated = 2021-07-01]{\c_term_ior}
+% \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2021-01-01 } { -1 }
+\cs_new_protected:Npn \c_term_ior { -1 \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3doc.dtx b/macros/latex/contrib/l3kernel/l3doc.dtx
new file mode 100644
index 0000000000..9c00e79283
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3doc.dtx
@@ -0,0 +1,4598 @@
+% \iffalse meta-comment
+%
+%% File: l3doc.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 l3docstrip.tex
+\askforoverwritefalse
+\preamble
+
+Copyright (C) 1990-2017,2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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{expl3,xparse,calc}
+%</driver|class>
+%
+%<*driver>
+\ProvidesFile{l3doc.dtx}[2017/03/18 L3 Experimental documentation class]
+\documentclass{l3doc}
+\usepackage{framed,lipsum}
+\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}{2018-04-30}{}
+ {L3 Experimental documentation class}
+%</class>
+% \fi
+%
+% \title{The \cls{l3doc} class}
+% \author{\Team}
+% \date{Released 2019-08-25}
+% \maketitle
+% \tableofcontents
+%
+% \begin{documentation}
+%
+%
+% \section{Introduction}
+%
+% 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.
+%
+% \textbf{It is much less stable than the main \pkg{expl3} packages.
+% Use at own risk!}
+%
+% 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{xparse} 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{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 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{l3docstrip} 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{l3docstrip} 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 sequencies entirely, use \texttt{cs-break-off}.
+% \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{syntax}
+% Two heavily-used environments are defined to describe the syntax of
+% \pkg{expl3} functions and variables.
+% \begin{framed}
+% \vspace{-\baselineskip}
+% \begin{verbatim}
+% \begin{function}{\function_one:, \function_two:}
+% \begin{syntax}
+% |\foo_bar:| \Arg{meta} \meta{test_1}
+% \end{syntax}
+% \meta{description}
+% \end{function}
+% \end{verbatim}
+% \hrulefill
+% \par
+% \hspace*{0.25\textwidth}
+% \begin{minipage}{0.5\textwidth}
+% \begin{function}{\function_one:, \function_two:}
+% \begin{syntax}
+% |\foo_bar:| \Arg{meta} \meta{test_1}
+% \end{syntax}
+% \meta{description}
+% \end{function}
+% \end{minipage}
+% \end{framed}
+%
+% Function environments take an optional argument to indicate whether
+% the function(s) it describes are expandable or restricted-expandable
+% or defined in conditional forms. Use |EXP|, |rEXP|, |TF|, |pTF|, or |noTF| for
+% this; 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|. As an example:
+% \begin{framed}
+% \vspace{-\baselineskip}
+% \begin{verbatim}
+% \begin{function}[pTF]{\cs_if_exist:N}
+% \begin{syntax}
+% \cs{cs_if_exist_p:N} \meta{cs}
+% \end{syntax}
+% \meta{description}
+% \end{function}
+% \end{verbatim}
+% \hrulefill
+% \par
+% \hspace*{0.25\textwidth}
+% \begin{minipage}{0.5\textwidth}
+% \begin{function}[pTF]{\cs_if_exist:N}
+% \begin{syntax}
+% \cs{cs_if_exist_p:N} \meta{cs}
+% \end{syntax}
+% \meta{description}
+% \end{function}
+% \end{minipage}
+% \end{framed}
+%
+% \DescribeEnv{variable}
+% If you are documenting a variable instead of a function, use the
+% \env{variable} environment instead; it behaves identically to the
+% \env{function} environment above.
+%
+% \DescribeEnv{texnote}
+% This environment is used to call out sections within \env{function}
+% and similar 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}).
+%
+%
+% \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_internal_set_bool,
+% \l_@@_macro_internal_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_internal_set_bool
+\bool_new:N \l_@@_macro_internal_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,
+% \str_case:fn,
+% \tl_count:f,
+% \tl_greplace_all:Nxn,
+% \tl_greplace_all:Nno,
+% \tl_if_head_eq_charcode:oNTF,
+% \tl_if_head_eq_charcode:oNT,
+% \tl_if_head_eq_charcode:oNF,
+% \tl_if_head_eq_meaning:VNF,
+% \tl_if_in:noTF,
+% \tl_if_in:ooTF,
+% \tl_if_in:NoTF,
+% \tl_if_in:NoT,
+% \tl_if_in:NoF,
+% \tl_remove_all:Nx,
+% \tl_replace_all:Nxn,
+% \tl_replace_all:Nnx,
+% \tl_replace_all:Non,
+% \tl_replace_all:Nno,
+% \tl_replace_once:Noo,
+% \tl_to_str:f,
+% \tl_to_str:o,
+% \prop_get:NxNTF,
+% \prop_put:Nxn,
+% \prop_gput:NVx,
+% }
+% A few missing variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \seq_set_split:Nnn { NoV }
+\cs_generate_variant:Nn \seq_gput_right:Nn { Nf }
+\cs_generate_variant:Nn \str_case:nn { fn }
+\cs_generate_variant:Nn \tl_count:n { f }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn { Nx , Nno }
+\cs_generate_variant:Nn \tl_if_empty:nTF { f }
+\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNTF { o }
+\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNT { o }
+\cs_generate_variant:Nn \tl_if_head_eq_charcode:nNF { o }
+\cs_generate_variant:Nn \tl_if_head_eq_meaning:nNF { V }
+\cs_generate_variant:Nn \tl_if_in:nnTF { no , oo }
+\cs_generate_variant:Nn \tl_if_in:NnTF { No }
+\cs_generate_variant:Nn \tl_if_in:NnT { No }
+\cs_generate_variant:Nn \tl_if_in:NnF { No }
+\cs_generate_variant:Nn \tl_remove_all:Nn { Nx }
+\cs_generate_variant:Nn \tl_replace_all:Nnn { Nx , Nnx, No , Nno }
+\cs_generate_variant:Nn \tl_replace_once:Nnn { Noo }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { NnV }
+\cs_generate_variant:Nn \tl_to_str:n { f , o }
+\cs_generate_variant:Nn \prop_get:NnNTF { Nx }
+\cs_generate_variant:Nn \prop_put:Nnn { Nx }
+\cs_generate_variant:Nn \prop_gput:Nnn { NVx }
+% \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 are 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:f { \tl_to_str:f {#1} } }
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+\cs_generate_variant:Nn \@@_if_almost_str:nT { V }
+% \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} }
+ \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:Npx \@@_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:Non \l_@@_detect_internals_tl { \token_to_str:N _ } { _ }
+ \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:nnxxx { 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 }
+ { 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
+% |x|-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
+ \tl_set:Nx #2
+ { \@@_split_function_do:on \l_@@_tmpa_tl { \@@_base_form_aux:nnN } }
+ }
+ { \tl_set:Nn #2 {#1} }
+ }
+\cs_new:Npx \@@_key_get_base_TF:nN #1#2
+ {
+ \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 :
+ \str_if_eq:nnTF { #1 ~ } { \exp_args }
+ { \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:x
+ {
+ \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 } { deprecated-function }
+ { The~deprecated~function(s)~'#1'~should~have~been~removed~on~#2. }
+\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}
+\DeclareOption { a5paper } { \@latexerr { Option~not~supported } { } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { full }
+ {
+ \bool_gset_true:N \g_@@_typeset_documentation_bool
+ \bool_gset_true:N \g_@@_typeset_implementation_bool
+ }
+\DeclareOption { onlydoc }
+ {
+ \bool_gset_true:N \g_@@_typeset_documentation_bool
+ \bool_gset_false:N \g_@@_typeset_implementation_bool
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { check }
+ { \bool_gset_true:N \g_@@_checkfunc_bool }
+\DeclareOption { nocheck }
+ { \bool_gset_false:N \g_@@_checkfunc_bool }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { checktest }
+ { \bool_gset_true:N \g_@@_checktest_bool }
+\DeclareOption { nochecktest }
+ { \bool_gset_false:N \g_@@_checktest_bool }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { kernel }
+ { \bool_gset_true:N \g_@@_kernel_bool }
+\DeclareOption { stdmodule }
+ { \bool_gset_false:N \g_@@_kernel_bool }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { cm-default }
+ { \bool_gset_false:N \g_@@_lmodern_bool }
+\DeclareOption { lm-default }
+ { \bool_gset_true:N \g_@@_lmodern_bool }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { cs-break-off }
+ { \bool_gset_false:N \g_@@_cs_break_bool }
+\DeclareOption { cs-break-nohyphen }
+ { \PassOptionsToPackage{nohyphen}{underscore} }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption { show-notes }
+ { \bool_gset_true:N \g_@@_show_notes_bool }
+\DeclareOption { hide-notes }
+ { \bool_gset_false:N \g_@@_show_notes_bool }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\DeclareOption* { \PassOptionsToClass { \CurrentOption } { article } }
+\ExecuteOptions { full, kernel, nocheck, nochecktest, 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:nT { l3doc.cfg }
+ {
+ \cs_if_exist:cF { ExplMakeTitle }
+ { \msg_info:nn { l3doc } { input-cfg } }
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\ProcessOptions
+% \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 somewhat 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:Nxx \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{xparse}
+% 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 }
+ { \@@_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
+ }
+% \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{#1} \texttt{\char`\}} }
+\providecommand\marg[1]{ \Arg{#1} }
+\providecommand\oarg[1]{ \texttt[ \meta{#1} \texttt] }
+\providecommand\parg[1]{ \texttt( \meta{#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{x}-type~argument~(in~plain~
+ \TeX{}~terms,~inside~an~\cs{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~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:Non \l_@@_cmd_tl \l_@@_tmpb_tl { _ }
+ \@@_replace_at_at:N \l_@@_cmd_tl
+ \tl_replace_all:Nno \l_@@_cmd_tl { _ } \l_@@_tmpb_tl
+ }
+% \end{macrocode}
+% \paragraph{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 }
+ {
+ \verbatim@font
+ \@@_if_almost_str:VT \l_@@_cmd_tl
+ {
+ \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}
+% \paragraph{Indexing}
+% \begin{macrocode}
+ \bool_if:NF \l_@@_cmd_noindex_bool
+ {
+ \quark_if_no_value:NF \l_@@_cmd_index_tl
+ {
+ \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
+ {
+ \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:Non \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 }
+ {
+ \color{black}
+ \itshape TF
+ \makebox[0pt][r]
+ {
+ \cs_if_exist:NT \Codedoc@explTF { \color{red} }
+ \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:xN}
+% 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
+ {
+ \tl_set:Nx #2 { \tl_to_str:n {#1} }
+ \tl_replace_all:Nxn #2 { \c_underscore_str } { / }
+ \tl_remove_all:Nx #2 { \c_backslash_str }
+ \tl_put_left:Nn #2 { doc/function// }
+ }
+\cs_generate_variant:Nn \@@_get_hyper_target:nN { o , x }
+% \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
+ {
+ \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:Nx \l_@@_tmpa_tl
+ { \iow_char:N \^^M \c_percent_str }
+ \tl_remove_all:Nx \l_@@_tmpa_tl { \tl_to_str:n { ^ ^ A } }
+ \tl_remove_all:Nx \l_@@_tmpa_tl { \iow_char:N \^^I }
+ \tl_remove_all:Nx \l_@@_tmpa_tl { \iow_char:N \^^M }
+ \@@_detect_internals:N \l_@@_tmpa_tl
+ \@@_replace_at_at:N \l_@@_tmpa_tl
+ \exp_args:NNx \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:Nx \@@_names_parse_aux:nnn
+ { \@@_base_form_aux:nnN {#1} {#2} #3 }
+ {#1} {#2}
+ }
+ }
+ {
+ \bool_if:NT \l_@@_macro_TF_bool
+ { \msg_error:nnx { 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 to \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:Nno \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:x { \g_@@_base_name_tl }
+ }
+ {
+ \seq_gset_eq:NN \g_@@_variants_seq #1
+ \seq_gpop:NN \g_@@_variants_seq \l_@@_tmpb_tl
+ \@@_names_typeset_auxii:x
+ { \g_@@_base_name_tl : \l_@@_tmpb_tl }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_names_typeset_auxii:n, \@@_names_typeset_auxii:x}
+% 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:xN
+ { \@@_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 { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_names_typeset_block:nN, \@@_names_typeset_block:xN}
+% 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 { x }
+% \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_set_bool
+ {
+ \bool_if:NTF \l_@@_macro_internal_bool
+ { \prg_return_true: } { \prg_return_false: }
+ }
+ {
+ \tl_if_empty:fTF
+ {
+ \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:NNx
+ \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 .code:n = { \@@_deprecated_on:n {#1} } ,
+ 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:nnxx { l3doc } { future-date }
+ { \tl_to_str:N \l_@@_macro_argument_tl }
+ {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_deprecated_on:n}
+% The date comparison function expects two dates in the YYYY-MM-DD
+% format (|-|~is not subtraction here).
+% Complain if a deprecated function should have been removed earlier.
+% In any case, mark it as internal to suppress the text
+% \enquote{documented on page \ldots{}}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_deprecated_on:n #1
+ {
+ \@@_date_set:Nn \l_@@_tmpa_tl {#1}
+ \exp_args:No \@@_date_compare:nNnT
+ { \l_@@_tmpa_tl } < { \c_sys_year_int - \c_sys_month_int - \c_sys_day_int }
+ {
+ \msg_error:nnxx { l3doc } { deprecated-function }
+ { \tl_to_str:N \l_@@_macro_argument_tl }
+ { \l_@@_tmpa_tl }
+ }
+ \bool_set_true:N \l_@@_macro_internal_bool
+ \bool_set_true:N \l_@@_macro_internal_set_bool
+ }
+% \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
+ \HD@savedestfalse
+ \HD@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:xN}
+% \begin{macro}{\@@_function_index:n, \@@_function_index:x}
+% |#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:x
+ { #1 \bool_if:NT #2 { \tl_to_str:n {TF} } }
+ \@@_function_label:xN {#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 { x }
+\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 { x }
+% \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 { } }
+ {
+ \textrm(
+ \seq_use:Nn \g_@@_variants_seq { \textrm| }
+ \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:xN
+ {
+ \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 { x }
+% \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 }
+ } ,
+ internal .value_forbidden:n = true ,
+ internal .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_internal_bool
+ \bool_set_true:N \l_@@_macro_internal_set_bool
+ } ,
+ int .value_forbidden:n = true ,
+ int .code:n =
+ {
+ \bool_set_true:N \l_@@_macro_internal_bool
+ \bool_set_true:N \l_@@_macro_internal_set_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
+ deprecated .code:n = { \@@_deprecated_on:n {#1} } ,
+ 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{xparse}. 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_internal_bool
+ \bool_set_false:N \l_@@_macro_internal_set_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
+ { \HD@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:}
+% 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}.
+% \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:Nf \g_@@_nested_names_seq
+ { \exp_after:wN \token_to_str:N \l_@@_macro_documented_tl }
+ }
+ }
+\cs_new_protected:Npn \@@_macro_save_names_aux:n #1
+ { \seq_gput_right:Nn \g_@@_nested_names_seq {#1} }
+% \end{macrocode}
+% \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
+ \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:xN
+ { \@@_get_function_name:n {#1} } #2
+ }
+ }
+\cs_generate_variant:Nn \@@_macro_typeset_block:nN { x }
+\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 { x }
+% \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:Nx \@@_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
+ {
+ \@@_if_macro_internal:nF {#1}
+ { \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}. Note the space |\ |.
+% |#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 \ } }
+ }
+ \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:xN
+ {
+ \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:Nno \l_@@_tmpa_tl
+ { ~ } { \c_catcode_other_space_tl }
+ \@@_macroname_prefix:o \l_@@_tmpa_tl
+ \@@_macroname_suffix:N #2
+ }
+ }
+\cs_new_protected:Npn \@@_macroname_prefix:n #1
+ {
+ \@@_if_macro_internal:nTF {#1}
+ { \@@_typeset_aux:n {#1} } {#1}
+ }
+\cs_generate_variant:Nn \@@_macroname_prefix:n { o }
+\cs_new_protected:Npn \@@_macroname_suffix:N #1
+ { \bool_if:NTF #1 { \@@_typeset_TF: } { } }
+% \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:Nxn
+ \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 { Nx }
+% \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:Nx \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:Non \l_@@_tmpa_tl
+ { \token_to_str:N _ } { \_ }
+ \texttt { \l_@@_tmpa_tl }
+ }
+\cs_new_protected:Npn \@@_print_end_definition:
+ {
+ \seq_set_map:NNn \l_@@_tmpa_seq
+ \g_@@_nested_names_seq
+ { \exp_not:n { \@@_macro_end_wrap_item:n {##1} } }
+ End~ 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
+ { ! \@@_if_macro_internal_p:n {##1} }
+ \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:xN
+ { \seq_item:Nn \g_@@_nested_names_seq { 1 } }
+ \l_@@_tmpa_tl
+ \exp_args:Nx \pageref { \l_@@_tmpa_tl } .
+ }
+ \seq_gclear:N \g_@@_nested_names_seq
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Misc}
+%
+% \begin{macro}{\DescribeOption}
+% For describing package options. Due to Joseph Wright. Name/usage
+% might change soon.
+% \begin{macrocode}
+\newcommand*{\DescribeOption}
+ {
+ \leavevmode
+ \@bsphack
+ \begingroup
+ \MakePrivateLetters
+ \Describe@Option
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\newcommand*{\Describe@Option}[1]
+ {
+ \endgroup
+ \marginpar{
+ \raggedleft
+ \PrintDescribeEnv{#1}
+ }
+ \SpecialOptionIndex{#1}
+ \@esphack
+ \ignorespaces
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\newcommand*{\SpecialOptionIndex}[1]
+ {
+ \@bsphack
+ \begingroup
+ \HD@target
+ \let\HDorg@encapchar\encapchar
+ \edef\encapchar usage
+ {
+ \HDorg@encapchar hdclindex{\the\c@HD@hypercount}{usage}
+ }
+ \index
+ {
+ #1\actualchar{\protect\ttfamily#1}~(option)
+ \encapchar usage
+ }
+ \index
+ {
+ options:\levelchar#1\actualchar{\protect\ttfamily#1}
+ \encapchar usage
+ }
+ \endgroup
+ \@esphack
+ }
+% \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{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, \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 }
+ }
+\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{.dtx} file, not \tn{input} on a \file{.tex}
+% file.
+%
+% \begin{macrocode}
+\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 }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_gset:Npn \@docinclude #1
+ {
+ \clearpage
+ \immediate\write\@mainaux{\string\@input{#1.aux}}
+ \@tempswatrue
+ \if@partsw
+ \@tempswafalse
+ \cs_set:Npx \@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:Npx \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}
+%
+% \begin{macrocode}
+\cs_gset:Npn \codeline@wrindex #1
+ {
+ \immediate\write\@indexfile
+ {
+ \string\indexentry{#1}
+ { \filesep \int_use:N \c@CodelineNo }
+ }
+ }
+\tl_gclear:N \filesep
+% \end{macrocode}
+% \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
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_show_functions_defined:
+ {
+ \bool_lazy_and:nnT
+ { \g_@@_typeset_implementation_bool } { \g_@@_checkfunc_bool }
+ {
+ \iow_term:x { \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_map_inline:Nn \g_doc_functions_seq
+ {
+ \seq_if_in:NnTF \g_doc_macros_seq {##1}
+ {
+ \tl_put_right:Nx \l_@@_doc_def_tl
+ { ##1 \iow_newline: }
+ \iow_now:Nn \g_@@_func_iow { > ~ ##1 }
+ }
+ {
+ \tl_put_right:Nx \l_@@_doc_undef_tl
+ { ##1 \iow_newline: }
+ \iow_now:Nn \g_@@_func_iow { ! ~ ##1 }
+ }
+ }
+ \seq_map_inline:Nn \g_doc_macros_seq
+ {
+ \seq_if_in:NnF \g_doc_functions_seq {##1}
+ {
+ \tl_put_right:Nx \l_@@_undoc_def_tl
+ { ##1 \iow_newline: }
+ \iow_now:Nn \g_@@_func_iow { ? ~ ##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:x { \c_@@_iow_separator_tl }
+ }
+ }
+\AtEndDocument { \@@_show_functions_defined: }
+% \end{macrocode}
+%
+% TODO: use \cs{iow_term:x}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_functions_typeout:nN #1#2
+ {
+ \tl_if_empty:NF #2
+ {
+ \typeout
+ {
+ \c_@@_iow_midrule_tl \iow_newline:
+ #1 \iow_newline:
+ \c_@@_iow_midrule_tl \iow_newline:
+ #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:Nx \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:Nx \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_set:Nn \tex_interactionmode:D { \l_@@_tmpa_int }
+ }
+ }
+ }
+\AtEndDocument { \@@_show_not_tested: }
+% \end{macrocode}
+%
+% \subsection{Indexing}
+%
+% \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:x
+ {
+ \@@_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:x
+ {
+ \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 }
+ { \codeline@wrindex }
+ {
+ \str_case:nnF {#6}
+ {
+ { main } { \codeline@wrindex }
+ { usage } { \index }
+ }
+ { \HD@target \index }
+ }
+ {
+ \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
+ }
+ \encapchar
+ hdclindex{\the\c@HD@hypercount}{#6}
+ }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_special_index_set:Nn #1#2
+ {
+ \tl_set:Nx #1 { \tl_to_str:n {#2} }
+ \@@_if_almost_str:nTF {#2}
+ {
+ \tl_replace_all:Non #1 { \tl_to_str:n { __ } }
+ {
+ \verbatimchar
+ \token_to_str:N \_ \token_to_str:N \_
+ \token_to_str:N \verb * \verbatimchar
+ }
+ \exp_args:Nx \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
+ }
+ }
+ \tl_set:Nx #1
+ {
+ \token_to_str:N \verb * \verbatimchar
+ #1 \verbatimchar
+ }
+ }
+ {
+ \tl_set:Nn #1 {#2}
+ \tl_replace_all:Non #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:Nxn #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:Npn \@@_key_get:n #1
+ {
+ \@@_key_get_base:nN {#1} \l_@@_index_macro_tl
+ \tl_set:Nx \l_@@_index_key_tl
+ { \tl_to_str:N \l_@@_index_macro_tl }
+ \tl_clear:N \l_@@_index_module_tl
+ \tl_if_in:NoTF \l_@@_index_key_tl { \tl_to_str:n { __ } }
+ { \bool_set_true:N \l_@@_index_internal_bool }
+ { \bool_set_false:N \l_@@_index_internal_bool }
+ \exp_last_unbraced:NNo
+ \tl_if_head_eq_charcode:oNT
+ { \l_@@_index_key_tl } \c_backslash_str
+ { \@@_key_pop: }
+ \tl_if_in:NoTF \l_@@_index_key_tl { \token_to_str:N : }
+ { \@@_key_func: }
+ {
+ \tl_if_in:NoTF \l_@@_index_key_tl { \token_to_str:N _ }
+ { \@@_key_var: }
+ {
+ \tl_if_in:NoT \l_@@_index_key_tl { \token_to_str:N @ }
+ { \tl_set:Nn \l_@@_index_module_tl { TeX } }
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_key_pop:
+ {
+ \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} }
+ \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:oNT { \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:oNT { \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. If there is no underscore left we had something
+% like \cs{c_zero} which we assume is an integer constant. If there
+% is one underscore we assume it is a variable like \cs{c_empty_tl}
+% whose module name is the last part. Otherwise the module name is
+% the part before any underscore.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_key_var:
+ {
+ \exp_args:Nx \tl_if_head_eq_charcode:nNTF
+ { \exp_args:No \str_tail:n \l_@@_index_key_tl } _
+ {
+ \str_case:fn { \str_head:N \l_@@_index_key_tl }
+ {
+ { q } { \tl_set:Nn \l_@@_index_module_tl { quark } }
+ { s } { \tl_set:Nn \l_@@_index_module_tl { quark } }
+ }
+ \@@_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
+ \tl_set:Nx \l_@@_index_module_tl
+ {
+ \int_case:nnF { \seq_count:N \l_@@_tmpa_seq }
+ {
+ { 0 } { }
+ { 1 } { int }
+ { 2 } { \seq_item:Nn \l_@@_tmpa_seq { 2 } }
+ }
+ { \seq_item:Nn \l_@@_tmpa_seq { 1 } }
+ }
+ }
+ }
+ {
+ \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}
+%
+% \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/macros/latex/contrib/l3kernel/l3docstrip.dtx b/macros/latex/contrib/l3kernel/l3docstrip.dtx
new file mode 100644
index 0000000000..722ed3f484
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3docstrip.dtx
@@ -0,0 +1,292 @@
+% \iffalse
+%
+%% File l3dosctrip.dtx
+%
+% Copyright (C) 2012,2014-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Code extraction and manipulation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Extending \textsf{DocStrip}}
+%
+% The \pkg{l3docstrip} module adds \LaTeX3 extensions to the \textsf{DocStrip}
+% program for extracting code from \texttt{.dtx}. As such, this documentation
+% should be read along with that for \textsf{DocStrip}.
+%
+% \section{Internal functions and variables}
+%
+% An important consideration for \LaTeX3 development is separating out public
+% and internal functions. Functions and variables which are private to one
+% module should not be used or modified by any other module. As \TeX{} does
+% not have any formal namespacing system, this requires a convention for
+% indicating which functions in a code-level module are public and which are
+% private.
+%
+% Using \pkg{l3docstrip} allows internal functions to be indicated using a
+% \enquote{two part} system. Within the \texttt{.dtx} file, internal functions
+% may be indicated using |@@| in place of the module name, for example
+% \begin{verbatim}
+% \cs_new_protected:Npn \@@_some_function:nn #1#2
+% {
+% % Some code here
+% }
+% \tl_new:N \l_@@_internal_tl
+% \end{verbatim}
+%
+% To extract the code using \pkg{l3docstrip}, the \enquote{guard} concept
+% used by \textsf{DocStrip} is extended by introduction of the syntax
+% \texttt{\%<@@=\meta{module}>}. The \meta{module} name then replaces
+% the |@@| when the code is extracted, so that
+% \begin{verbatim}
+% %<*package>
+% %<@@=foo>
+% \cs_new_protected:Npn \@@_some_function:nn #1#2
+% {
+% % Some code here
+% }
+% \tl_new:N \l_@@_internal_tl
+% %</package>
+% \end{verbatim}
+% is extracted as
+% \begin{verbatim}
+% \cs_new_protected:Npn \__foo_some_function:nn #1#2
+% {
+% % Some code here
+% }
+% \tl_new:N \l__foo_internal_tl
+% \end{verbatim}
+% where the |__| indicates that the functions and variables are internal
+% to the \texttt{foo} module.
+%
+% Use |@@@@| to obtain |@@| in the output (|@@@@@| to get |@@@|). For
+% longer pieces of code the replacement can be completely suppressed by
+% giving an empty module name, namely using the syntax \texttt{\%<@@=>}.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3docstrip} implementation}
+%
+% \begin{macrocode}
+%<*program>
+% \end{macrocode}
+%
+% We start by loading the existing \textsf{DocStrip} code using the \TeX{}
+% input convention.
+% \begin{macrocode}
+\input docstrip %
+% \end{macrocode}
+%
+% \begin{macro}{\checkOption}
+% The \cs{checkOption} macro is defined by \textsf{DocStrip} and is redefined
+% here to accommodate the new \texttt{\%<@} syntax.
+%
+% When the macros that process a line have found that the line starts with
+% \enquote{\texttt{\%<}}, a guard line has been encountered. The first
+% character of a guard can be an asterisk (\texttt{*}), a slash (\texttt{/}),
+% a plus (\texttt{+}), a minus (\texttt{-}), a less-than sign (\texttt{<})
+% starting verbatim mode, a commercial at (\texttt{@}) or any other character
+% that can be found in an option name. This means that we have to peek at the
+% next token and decide what kind of guard we have. We reinsert |#1| as it
+% may be needed by \cs{doOption}.
+% \begin{macrocode}
+\def\checkOption<#1{%
+ \ifcase
+ \ifx*#10\else \ifx/#11\else
+ \ifx+#12\else \ifx-#13\else
+ \ifx<#14\else \ifx @#15\else 6\fi\fi\fi\fi\fi\fi\relax
+ \expandafter\starOption\or
+ \expandafter\slashOption\or
+ \expandafter\plusOption\or
+ \expandafter\minusOption\or
+ \expandafter\verbOption\or
+ \expandafter\moduleOption\or
+ \expandafter\doOption\fi
+ #1%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\moduleOption}
+% In the case where the line starts |%<@|: the defined syntax requires that
+% this continues to |%<@@=|. At the moment, we assume that the syntax is
+% correct and |#1| here is the module name for substitution into any
+% internal functions in the extracted material.
+% \begin{macrocode}
+\def\moduleOption @@=#1>#2\endLine{%
+ \maybeMsg{<@@=#1>}%
+ \prepareActiveModule{#1}%
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\prepareActiveModule}
+% \begin{macro}{\replaceModuleInLine}
+% Here, we set up to do the search-and-replace when doing the extraction.
+% The argument (|#1|) is the replacement text to use, or if empty an
+% indicator that no replacement should be done. The search material is
+% one of |__@@|, |_@@| or |@@|, done in order such that all three end
+% up the same in the output. The string |@@@@| is hidden from these
+% replacements by temporarily turning it into a pair of letters with
+% different category codes, not produced by \pkg{docstrip}; this allows to
+% get |@@| in the output. The replacement function is initialised as a
+% do-nothing for the case where |%<@@=| is never seen.
+% \begin{macrocode}
+\begingroup
+ \catcode`\_ = 12 %
+ \long\gdef\prepareActiveModule#1{%
+ \ifx\relax#1\relax
+ \let\replaceModuleInLine\empty
+ \else
+ \edef\replaceModuleInLine{%
+ \noexpand\replaceAllIn\noexpand\inLine{@@@@}{\string aa}%
+ \noexpand\replaceAllIn\noexpand\inLine{__@@}{__#1}%
+ \noexpand\replaceAllIn\noexpand\inLine{_@@}{__#1}%
+ \noexpand\replaceAllIn\noexpand\inLine{@@}{__#1}%
+ \noexpand\replaceAllIn\noexpand\inLine{\string aa}{@@}%
+ }%
+ \fi
+ }
+\endgroup
+\let\replaceModuleInLine\empty
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\replaceAllIn}
+% \begin{macro}{\replaceAllInAuxI}
+% \begin{macro}{\replaceAllInAuxII}
+% \begin{macro}{\replaceAllInAuxIII}
+% The code here is a simple search-and-replace routine for a macro |#1|,
+% replacing |#2| by |#3|. As set up here, there is an assumption that nothing
+% is going to be expandable, which is reasonable as \pkg{l3docstrip} deals
+% with \enquote{string} material.
+% \begin{macrocode}
+\long\def\replaceAllIn#1#2#3{%
+ \long\def\tempa##1##2#2{%
+ ##2\qMark\replaceAllInAuxIII#3##1%
+ }%
+ \edef#1{\expandafter\replaceAllInAuxI#1\qMark#2\qStop}%
+}
+\def\replaceAllInAuxI{%
+ \expandafter\replaceAllInAuxII\tempa\replaceAllInAuxI\empty
+}
+\long\def\replaceAllInAuxII#1\qMark#2{#1}
+\long\def\replaceAllInAuxIII#1\qStop{}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\normalLine}
+% The \cs{normalLine} macro is present in \textsf{DocStrip} but is modified
+% here to include the search-and-replace macro \cs{replaceModuleInLine}. The
+% macro \cs{normalLine} writes its argument (which has to be delimited with
+% |\endLine|) on all active output files, i.e.~those with off-counters equal
+% to zero. The counter \cs{codeLinesPassed} is incremented by~$1$ for
+% statistics (the guards for this used in \textsf{DocStrip} are retained).
+% \begin{macrocode}
+\def\normalLine#1\endLine{%
+%<*stats>
+ \advance\codeLinesPassed\@ne
+%</stats>
+ \maybeMsg{.}%
+ \def\inLine{#1}%
+ \replaceModuleInLine
+ \let\do\putline@do
+ \activefiles
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\doOption}
+% As \textsf{DocStrip} processes each line, a line starting with |%<| triggers (via \cs{checkOption} above) a look-ahead to establish the type of guard encountered.
+% Of the branching possibilities, if we ignore the deprecated |+| and |-| and choose to do no replacement for the special verbatim mode, the only branch that we need to alter is that for a single-line guard such as\\ | %<stats> ...stats-only code to include...|.
+% The original definition of \cs{doOption} did not use \cs{inLine} but I believe we're safe to do so.
+% \begin{macrocode}
+\def\doOption#1>#2\endLine{%
+ \maybeMsg{<#1 . >}%
+ \Evaluate{#1}%
+ \def\do##1##2##3{%
+ \if1\Expr{##2}%
+ \def\inLine{#2}%
+ \replaceModuleInLine
+ \StreamPut##1{\inLine}%
+ \fi
+ }%
+ \activefiles
+}
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</program>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3docstrip.pdf b/macros/latex/contrib/l3kernel/l3docstrip.pdf
new file mode 100644
index 0000000000..0327b773d4
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3docstrip.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3expan.dtx b/macros/latex/contrib/l3kernel/l3expan.dtx
new file mode 100644
index 0000000000..a8101b0f6c
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3expan.dtx
@@ -0,0 +1,2804 @@
+% \iffalse meta-comment
+%
+%% File: l3expan.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Argument expansion^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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. 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 can only be applied if
+% the \meta{parent control sequence} is already defined. 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.
+%
+% 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}.
+%
+% 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}
+%
+% \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 was added in May 2018.
+% In recent enough engines (starting around 2019) it relies on the
+% primitive \tn{expanded} hence is fast. In older engines it is very
+% much slower. As a result it should only be used in performance
+% critical code if typical users will have a recent installation of the
+% \TeX{} ecosystem.
+%
+% 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 |x| or |e|
+% 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) or very much slower in
+% old engines (for |e| 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). The drawback is that
+% |e|~expansion is very much slower in old engines (before 2019).
+% Consider using |f|~expansion if that type of expansion is
+% sufficient to perform the required expansion, or |x|~expansion if
+% the variant will not itself need to be expandable.
+% \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 internal functions for argument expansion
+% come in two flavours, some faster than others.
+% \begin{itemize}
+% \item
+% Arguments that might need expansion should come first in the list
+% of arguments.
+% \item
+% Arguments that should consist of single tokens |N|, |c|, |V|, or
+% |v| should come first among these.
+% \item
+% Arguments that appear after the first multi-token argument |n|,
+% |f|, |e|, or |o| require slightly slower special processing to be
+% expanded. Therefore it is best to use the optimized functions,
+% namely those that contain only |N|, |c|, |V|, and |v|, and, in the
+% last position, |o|, |f|, |e|, with possible trailing |N| or |n| or
+% |T| or |F|, which are not expanded. Any |x|-type argument causes
+% slightly slower processing.
+% \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}.
+% \begin{texnote}
+% Protected macros that appear in a \texttt{c}-type argument are
+% expanded despite being protected; \cs{exp_not:n} also has no
+% effect. An internal error occurs if non-characters or active
+% characters remain after full expansion, as the conversion to a
+% control sequence is not possible.
+% \end{texnote}
+% \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.
+% \begin{texnote}
+% Protected macros that appear in a \texttt{v}-type argument are
+% expanded despite being protected; \cs{exp_not:n} also has no
+% effect. An internal error occurs if non-characters or active
+% characters remain after full expansion, as the conversion to a
+% control sequence is not possible.
+% \end{texnote}
+% \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.
+% \begin{texnote}
+% This relies on the \tn{expanded} primitive when available (in
+% \LuaTeX{} and starting around 2019 in other engines). Otherwise
+% it uses some fall-back code that is very much slower. As a result
+% it should only be used in performance-critical code if typical
+% users have a recent installation of the \TeX{} ecosystem.
+% \end{texnote}
+% \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,
+% }
+% \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.
+% These functions need slower processing.
+% \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: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,
+% }
+% \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}.
+% These functions need slower processing.
+% \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: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_arg:N} function.
+% \begin{texnote}
+% This is the \TeX{} primitive \tn{expandafter} renamed.
+% \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 |x|-type argument or the first
+% token in an |o| or |e| or |f| argument.
+% \begin{texnote}
+% This is the \TeX{} \tn{noexpand} primitive. 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 |x|-expanding definition
+% (\cs{cs_new:Npx}), 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}.
+% \begin{texnote}
+% Protected macros that appear in a \texttt{c}-type argument are
+% expanded despite being protected; \cs{exp_not:n} also has no
+% effect. An internal error occurs if non-characters or active
+% characters remain after full expansion, as the conversion to a
+% control sequence is not possible.
+% \end{texnote}
+% \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| 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{} \tn{unexpanded} primitive. In an
+% |x|-expanding definition (\cs{cs_new:Npx}), \cs{exp_not:n}~|{#1}|
+% is equivalent to |##1| rather than to~|#1|, namely it inserts the
+% two characters |#| and~|1|. In an |e|-type argument
+% \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 |x|-type or \texttt{e}-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 |x|-type or \texttt{e}-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 |x|-type or \texttt{e}-type arguments is prevented using \cs{exp_not:n}.
+% \begin{texnote}
+% Protected macros that appear in a \texttt{v}-type argument are
+% expanded despite being protected; \cs{exp_not:n} also has no
+% effect. An internal error occurs if non-characters or active
+% characters remain after full expansion, as the conversion to a
+% control sequence is not possible.
+% \end{texnote}
+% \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| 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 |x|-type or \texttt{e}-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{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}
+%<*initex|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. Otherwise
+% defer to \cs{exp_args:Ne} implemented later.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_expanded:D
+ {
+ \cs_new:Npn \::e #1 \::: #2#3
+ { \tex_expanded:D { \exp_not:n { #1 \::: } { \exp_not:n {#2} {#3} } } }
+ }
+ {
+ \cs_new:Npn \::e #1 \::: #2#3
+ { \exp_args:Ne \@@_arg_next:nnn {#3} {#1} {#2} }
+ }
+% \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 |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:Npx \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:
+ \__kernel_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. Otherwise
+% use \cs{@@_e:nn}, defined later, to fully expand tokens.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_expanded:D
+ {
+ \cs_new:Npn \exp_args:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D { {#2} } }
+ }
+ {
+ \cs_new:Npn \exp_args:Ne #1#2
+ {
+ \exp_after:wN #1 \exp_after:wN
+ { \exp:w \@@_e:nn {#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_if_exist:NTF \tex_expanded:D
+ {
+ \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:NNe { \::N \::e \::: } }
+\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: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: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}
+%
+% \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_args:Nx}
+% \begin{macrocode}
+\cs_new_protected:Npn \exp_args:Nx { \::x \::: }
+% \end{macrocode}
+% \end{macro}
+%
+% \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,
+% }
+% \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.
+% \begin{macrocode}
+\cs_new:Npn \exp_args:Nnc { \::n \::c \::: }
+\cs_new:Npn \exp_args:Nno { \::n \::o \::: }
+\cs_new:Npn \exp_args:NnV { \::n \::V \::: }
+\cs_new:Npn \exp_args:Nnv { \::n \::v \::: }
+\cs_new:Npn \exp_args:Nne { \::n \::e \::: }
+\cs_new:Npn \exp_args:Nnf { \::n \::f \::: }
+\cs_new:Npn \exp_args:Noc { \::o \::c \::: }
+\cs_new:Npn \exp_args:Noo { \::o \::o \::: }
+\cs_new:Npn \exp_args:Nof { \::o \::f \::: }
+\cs_new:Npn \exp_args:NVo { \::V \::o \::: }
+\cs_new:Npn \exp_args:Nfo { \::f \::o \::: }
+\cs_new:Npn \exp_args:Nff { \::f \::f \::: }
+\cs_new_protected:Npn \exp_args:NNx { \::N \::x \::: }
+\cs_new_protected:Npn \exp_args:Ncx { \::c \::x \::: }
+\cs_new_protected:Npn \exp_args:Nnx { \::n \::x \::: }
+\cs_new_protected:Npn \exp_args:Nox { \::o \::x \::: }
+\cs_new_protected:Npn \exp_args:Nxo { \::x \::o \::: }
+\cs_new_protected:Npn \exp_args:Nxx { \::x \::x \::: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \exp_args:NNNv, \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,
+% }
+% \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}
+\cs_new:Npn \exp_args:NNNv { \::N \::N \::v \::: }
+\cs_new:Npn \exp_args:NNcf { \::N \::c \::f \::: }
+\cs_new:Npn \exp_args:NNno { \::N \::n \::o \::: }
+\cs_new:Npn \exp_args:NNnV { \::N \::n \::V \::: }
+\cs_new:Npn \exp_args:NNoo { \::N \::o \::o \::: }
+\cs_new:Npn \exp_args:NNVV { \::N \::V \::V \::: }
+\cs_new:Npn \exp_args:Ncno { \::c \::n \::o \::: }
+\cs_new:Npn \exp_args:NcnV { \::c \::n \::V \::: }
+\cs_new:Npn \exp_args:Ncoo { \::c \::o \::o \::: }
+\cs_new:Npn \exp_args:NcVV { \::c \::V \::V \::: }
+\cs_new:Npn \exp_args:Nnnc { \::n \::n \::c \::: }
+\cs_new:Npn \exp_args:Nnno { \::n \::n \::o \::: }
+\cs_new:Npn \exp_args:Nnnf { \::n \::n \::f \::: }
+\cs_new:Npn \exp_args:Nnff { \::n \::f \::f \::: }
+\cs_new:Npn \exp_args:Nooo { \::o \::o \::o \::: }
+\cs_new:Npn \exp_args:Noof { \::o \::o \::f \::: }
+\cs_new:Npn \exp_args:Nffo { \::f \::f \::o \::: }
+\cs_new_protected:Npn \exp_args:NNNx { \::N \::N \::x \::: }
+\cs_new_protected:Npn \exp_args:NNnx { \::N \::n \::x \::: }
+\cs_new_protected:Npn \exp_args:NNox { \::N \::o \::x \::: }
+\cs_new_protected:Npn \exp_args:Nnnx { \::n \::n \::x \::: }
+\cs_new_protected:Npn \exp_args:Nnox { \::n \::o \::x \::: }
+\cs_new_protected:Npn \exp_args:Nccx { \::c \::c \::x \::: }
+\cs_new_protected:Npn \exp_args:Ncnx { \::c \::n \::x \::: }
+\cs_new_protected:Npn \exp_args:Noox { \::o \::o \::x \::: }
+% \end{macrocode}
+% \end{macro}
+% \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_if_exist:NTF \tex_expanded:D
+ {
+ \cs_new:Npn \::e_unbraced \::: #1#2
+ { \tex_expanded:D { \exp_not:n {#1} #2 } }
+ }
+ {
+ \cs_new:Npn \::e_unbraced \::: #1#2
+ { \exp:w \@@_e:nn {#2} {#1} }
+ }
+\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:Npx \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: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: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_if_exist:NTF \tex_expanded:D
+ {
+ \cs_new:Npn \exp_last_unbraced:Ne #1#2
+ { \exp_after:wN #1 \tex_expanded:D {#2} }
+ }
+ { \cs_new:Npn \exp_last_unbraced:Ne { \::e_unbraced \::: } }
+\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: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 \tn{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_if_exist:NTF \tex_expanded:D
+ {
+ \cs_new:Npn \exp_not:e #1
+ { \__kernel_exp_not:w \tex_expanded:D { {#1} } }
+ }
+ {
+ \cs_new:Npn \exp_not:e
+ { \__kernel_exp_not:w \exp_args:Ne \prg_do_nothing: }
+ }
+\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 ^^@
+ { \__kernel_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{Emulating \texttt{e}-type expansion}
+%
+% When the \tn{expanded} primitive is available it is used to implement
+% \texttt{e}-type expansion; otherwise we emulate it.
+%
+% \begin{macrocode}
+\cs_if_exist:NF \tex_expanded:D
+ {
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_e:nn, \@@_e_end:nn}
+% Repeatedly expand tokens, keeping track of fully-expanded tokens in
+% the second argument to \cs{@@_e:nn}; this function eventually
+% calls \cs{@@_e_end:nn} to leave \cs{exp_end:} in the input
+% stream, followed by the result of the expansion. There are many
+% special cases: spaces, brace groups, \tn{noexpand}, \tn{unexpanded},
+% \tn{the}, \tn{primitive}.
+% While we use brace tricks \cs{if_false:} |{| \cs{fi:}, the expansion
+% of this function is always triggered by \cs{exp:w} so brace balance
+% is eventually restored after that is hit with a single step of
+% expansion. Otherwise we could not nest \texttt{e}-type expansions
+% within each other.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e:nn #1
+ {
+ \if_false: { \fi:
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_e:N }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_e_group:n }
+ {
+ \tl_if_empty:nTF {#1}
+ { \exp_after:wN \@@_e_end:nn }
+ { \exp_after:wN \@@_e_space:nn }
+ \exp_after:wN { \if_false: } \fi:
+ }
+ }
+ #1
+ }
+ }
+ \cs_new:Npn \@@_e_end:nn #1#2 { \exp_end: #2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_space:nn}
+% For an explicit space character, remove it by \texttt{f}-expansion
+% and put it in the (future) output.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_space:nn #1#2
+ { \exp_args:Nf \@@_e:nn {#1} { #2 ~ } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_group:n, \@@_e_put:nn, \@@_e_put:nnn}
+% For a group, expand its contents, wrap it in two pairs of braces,
+% and call \cs{@@_e_put:nn}. This function places the first item (the
+% double-brace wrapped result) into the output. Importantly,
+% \cs{tl_head:n} works even if the input contains quarks.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_group:n #1
+ {
+ \exp_after:wN \@@_e_put:nn
+ \exp_after:wN { \exp_after:wN { \exp_after:wN {
+ \exp:w \if_false: } \fi: \@@_e:nn {#1} { } } }
+ }
+ \cs_new:Npn \@@_e_put:nn #1
+ {
+ \exp_args:NNo \exp_args:No \@@_e_put:nnn
+ { \tl_head:n {#1} } {#1}
+ }
+ \cs_new:Npn \@@_e_put:nnn #1#2#3
+ { \exp_args:No \@@_e:nn { \use_none:n #2 } { #3 #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_e:N, \@@_e:Nnn, \@@_e_protected:Nnn, \@@_e_expandable:Nnn}
+% For an \texttt{N}-type token, call \cs{@@_e:Nnn} with arguments the
+% \meta{first token}, the remaining tokens to expand and what's
+% already been expanded. If the \meta{first token} is non-expandable,
+% including \tn{protected} (\tn{long} or not) macros, it is put in the
+% result by \cs{@@_e_protected:Nnn}. The four special primitives
+% \tn{unexpanded}, \tn{noexpand}, \tn{the}, \tn{primitive} are
+% detected; otherwise the token is expanded by
+% \cs{@@_e_expandable:Nnn}.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e:N #1
+ {
+ \exp_after:wN \@@_e:Nnn
+ \exp_after:wN #1
+ \exp_after:wN { \if_false: } \fi:
+ }
+ \cs_new:Npn \@@_e:Nnn #1
+ {
+ \if_case:w
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1 1 ~ \fi:
+ \token_if_protected_macro:NT #1 { 1 ~ }
+ \token_if_protected_long_macro:NT #1 { 1 ~ }
+ \if_meaning:w \exp_not:n #1 2 ~ \fi:
+ \if_meaning:w \exp_not:N #1 3 ~ \fi:
+ \if_meaning:w \tex_the:D #1 4 ~ \fi:
+ \if_meaning:w \tex_primitive:D #1 5 ~ \fi:
+ 0 ~
+ \exp_after:wN \@@_e_expandable:Nnn
+ \or: \exp_after:wN \@@_e_protected:Nnn
+ \or: \exp_after:wN \@@_e_unexpanded:Nnn
+ \or: \exp_after:wN \@@_e_noexpand:Nnn
+ \or: \exp_after:wN \@@_e_the:Nnn
+ \or: \exp_after:wN \@@_e_primitive:Nnn
+ \fi:
+ #1
+ }
+ \cs_new:Npn \@@_e_protected:Nnn #1#2#3
+ { \@@_e:nn {#2} { #3 #1 } }
+ \cs_new:Npn \@@_e_expandable:Nnn #1#2
+ { \exp_args:No \@@_e:nn { #1 #2 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_e_primitive:Nnn,
+% \@@_e_primitive_aux:NNw,
+% \@@_e_primitive_aux:NNnn,
+% \@@_e_primitive_other:NNnn,
+% \@@_e_primitive_other_aux:nNNnn
+% }
+% We don't try hard to make sensible error recovery since the error
+% recovery of \cs{tex_primitive:D} when followed by something else
+% than a primitive depends on the engine. The only valid case is when
+% what follows is \texttt{N}-type. Then distinguish special
+% primitives \tn{unexpanded}, \tn{noexpand}, \tn{the}, \tn{primitive}
+% from other primitives. In the \enquote{other} case, the only
+% reasonable way to check if the primitive that follows
+% \cs{tex_primitive:D} is expandable is to expand and compare the
+% before-expansion and after-expansion results. If they coincide then
+% probably the primitive is non-expandable and should be put in the
+% output together with \cs{tex_primitive:D} (one can cook up contrived
+% counter-examples where the true \tn{expanded} would have an infinite
+% loop), and otherwise one should continue expanding.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_primitive:Nnn #1#2
+ {
+ \if_false: { \fi:
+ \tl_if_head_is_N_type:nTF {#2}
+ { \@@_e_primitive_aux:NNw #1 }
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { e-type }
+ { Missing~primitive~name }
+ \@@_e_primitive_aux:NNw #1 \c_empty_tl
+ }
+ #2
+ }
+ }
+ \cs_new:Npn \@@_e_primitive_aux:NNw #1#2
+ {
+ \exp_after:wN \@@_e_primitive_aux:NNnn
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN { \if_false: } \fi:
+ }
+ \cs_new:Npn \@@_e_primitive_aux:NNnn #1#2
+ {
+ \exp_args:Nf \str_case_e:nnTF { \cs_to_str:N #2 }
+ {
+ { unexpanded } { \@@_e_unexpanded:Nnn \exp_not:n }
+ { noexpand } { \@@_e_noexpand:Nnn \exp_not:N }
+ { the } { \@@_e_the:Nnn \tex_the:D }
+ {
+ \sys_if_engine_xetex:T { pdf }
+ \sys_if_engine_luatex:T { pdf }
+ primitive
+ } { \@@_e_primitive:Nnn #1 }
+ }
+ { \@@_e_primitive_other:NNnn #1 #2 }
+ }
+ \cs_new:Npn \@@_e_primitive_other:NNnn #1#2#3
+ {
+ \exp_args:No \@@_e_primitive_other_aux:nNNnn
+ { #1 #2 #3 }
+ #1 #2 {#3}
+ }
+ \cs_new:Npn \@@_e_primitive_other_aux:nNNnn #1#2#3#4#5
+ {
+ \str_if_eq:nnTF {#1} { #2 #3 #4 }
+ { \@@_e:nn {#4} { #5 #2 #3 } }
+ { \@@_e:nn {#1} {#5} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_noexpand:Nnn}
+% The \tn{noexpand} primitive has no effect when followed by a token
+% that is not \texttt{N}-type; otherwise \cs{@@_e_put:nn} can grab the
+% next token and put it in the result unchanged.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_noexpand:Nnn #1#2
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \@@_e_put:nn } { \@@_e:nn } {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_e_unexpanded:Nnn, \@@_e_unexpanded:nn,
+% \@@_e_unexpanded:nN, \@@_e_unexpanded:N
+% }
+% The \tn{unexpanded} primitive expands and ignores any space,
+% \cs{scan_stop:}, or token affected by \cs{exp_not:N}, then expects a
+% brace group. Since we only support brace-balanced token lists it is
+% impossible to support the case where the argument of \tn{unexpanded}
+% starts with an implicit brace. Even though we want to expand and
+% ignore spaces we cannot blindly \texttt{f}-expand because tokens
+% affected by \cs{exp_not:N} should discarded without being expanded
+% further.
+%
+% As usual distinguish four cases: brace group (the normal case, where
+% we just put the item in the result), space (just \texttt{f}-expand
+% to remove the space), empty (an error), or \texttt{N}-type
+% \meta{token}. In the last case call \cs{@@_e_unexpanded:nN}
+% triggered by an \texttt{f}-expansion. Having a non-expandable
+% \meta{token} after \tn{unexpanded} is an error (we recover by
+% passing |{}| to \tn{unexpanded}; this is different from \TeX{}
+% because the error recovery of \tn{unexpanded} changes the balance of
+% braces), unless that \meta{token} is \cs{scan_stop:} or a space
+% (recall that we don't implement the case of an implicit begin-group
+% token). An expandable \meta{token} is instead expanded, unless it
+% is \tn{noexpand}. The latter primitive can be followed by an expandable
+% \texttt{N}-type token (removed), by a non-expandable one (kept
+% and later causing an error), by a space (removed by
+% \texttt{f}-expansion), or by a brace group or nothing (later causing
+% an error).
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_unexpanded:Nnn #1 { \@@_e_unexpanded:nn }
+ \cs_new:Npn \@@_e_unexpanded:nn #1
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ {
+ \exp_args:Nf \@@_e_unexpanded:nn
+ { \@@_e_unexpanded:nN {#1} #1 }
+ }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_e_put:nn }
+ {
+ \tl_if_empty:nTF {#1}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { e-type }
+ { \unexpanded missing~brace }
+ \@@_e_end:nn
+ }
+ { \exp_args:Nf \@@_e_unexpanded:nn }
+ }
+ {#1}
+ }
+ }
+ \cs_new:Npn \@@_e_unexpanded:nN #1#2
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #2 #2
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \token_if_eq_catcode:NNTF #2 \c_space_token
+ { \exp_stop_f: }
+ {
+ \token_if_eq_meaning:NNTF #2 \scan_stop:
+ { \exp_stop_f: }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { e-type }
+ { \unexpanded missing~brace }
+ { }
+ }
+ }
+ }
+ {
+ \token_if_eq_meaning:NNTF #2 \exp_not:N
+ {
+ \exp_args:No \tl_if_head_is_N_type:nT { \use_none:n #1 }
+ { \@@_e_unexpanded:N }
+ }
+ { \exp_after:wN \exp_stop_f: #2 }
+ }
+ }
+ \cs_new:Npn \@@_e_unexpanded:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1 \else:
+ \exp_after:wN \use_i:nn
+ \fi:
+ \exp_stop_f: #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_the:Nnn, \@@_e_the:N, \@@_e_the_toks_reg:N}
+% Finally implement \tn{the}. Followed by anything other than an
+% \texttt{N}-type \meta{token} this causes an error (we just let
+% \TeX{} make one), otherwise we test the \meta{token}. If the
+% \meta{token} is expandable, expand it. Otherwise it could be any
+% kind of register, or things like \tn{numexpr}, so there is no way to
+% deal with all cases. Thankfully, only \tn{toks} data needs to be
+% protected from expansion since everything else gives a string of
+% characters. If the \meta{token} is \tn{toks} we find a number and
+% unpack using the |the_toks| functions. If it is a token register we
+% unpack it in a brace group and call \cs{@@_e_put:nn} to move it to
+% the result. Otherwise we unpack and continue expanding (useless but
+% safe) since it is basically impossible to have a handle on where the
+% result of \tn{the} ends.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_the:Nnn #1#2
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \if_false: { \fi: \@@_e_the:N #2 } }
+ { \exp_args:No \@@_e:nn { \tex_the:D #2 } }
+ }
+ \cs_new:Npn \@@_e_the:N #1
+ {
+ \exp_after:wN \if_meaning:w \exp_not:N #1 #1
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ \if_meaning:w \tex_toks:D #1
+ \exp_after:wN \@@_e_the_toks:wnn \int_value:w
+ \exp_after:wN \@@_e_the_toks:n
+ \exp_after:wN { \int_value:w \if_false: } \fi:
+ \else:
+ \@@_e_if_toks_register:NTF #1
+ { \exp_after:wN \@@_e_the_toks_reg:N }
+ {
+ \exp_after:wN \@@_e:nn \exp_after:wN {
+ \tex_the:D \if_false: } \fi:
+ }
+ \exp_after:wN #1
+ \fi:
+ }
+ {
+ \exp_after:wN \@@_e_the:Nnn \exp_after:wN ?
+ \exp_after:wN { \exp:w \if_false: } \fi:
+ \exp_after:wN \exp_end: #1
+ }
+ }
+ \cs_new:Npn \@@_e_the_toks_reg:N #1
+ {
+ \exp_after:wN \@@_e_put:nn \exp_after:wN {
+ \exp_after:wN {
+ \tex_the:D \if_false: } \fi: #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_the_toks:wnn, \@@_e_the_toks:n, \@@_e_the_toks:N}
+% The calling function has applied \cs{int_value:w} so we collect
+% digits with \cs{@@_e_the_toks:n} (which gets the token list as an
+% argument) and \cs{@@_e_the_toks:N} (which gets the first token in
+% case it is \texttt{N}-type). The digits are themselves collected
+% into an \cs{int_value:w} argument to \cs{@@_e_the_toks:wnn}. Then
+% that function unpacks the \tn{toks}\meta{number} into the result.
+% We include |?| because \cs{@@_e_put:nnn} removes one item from its
+% second argument. Note that our approach is rather crude: in cases
+% like |\the\toks12~34| the first \cs{int_value:w} removes the space
+% and we will incorrectly unpack the |\the\toks1234|.
+% \begin{macrocode}
+ \cs_new:Npn \@@_e_the_toks:wnn #1; #2
+ {
+ \exp_args:No \@@_e_put:nnn
+ { \tex_the:D \tex_toks:D #1 } { ? #2 }
+ }
+ \cs_new:Npn \@@_e_the_toks:n #1
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \exp_after:wN \@@_e_the_toks:N \if_false: { \fi: #1 } }
+ { ; {#1} }
+ }
+ \cs_new:Npn \@@_e_the_toks:N #1
+ {
+ \if_int_compare:w 10 < 9 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {
+ #1
+ \exp_after:wN \@@_e_the_toks:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+ {
+ \exp_after:wN ;
+ \exp_after:wN { \if_false: } \fi: #1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_e_if_toks_register:NTF}
+% \begin{macro}[EXP]
+% {
+% \@@_e_the_XeTeXinterchartoks:,
+% \@@_e_the_errhelp:,
+% \@@_e_the_everycr:,
+% \@@_e_the_everydisplay:,
+% \@@_e_the_everyeof:,
+% \@@_e_the_everyhbox:,
+% \@@_e_the_everyjob:,
+% \@@_e_the_everymath:,
+% \@@_e_the_everypar:,
+% \@@_e_the_everyvbox:,
+% \@@_e_the_output:,
+% \@@_e_the_pdfpageattr:,
+% \@@_e_the_pdfpageresources:,
+% \@@_e_the_pdfpagesattr:,
+% \@@_e_the_pdfpkmode:
+% }
+% We need to detect both \tn{toks} registers like \tn{toks@} in
+% \LaTeXe{} and parameters such as \tn{everypar}, as the result of
+% unpacking the register should not expand further. Registers are
+% found by \cs{token_if_toks_register:NTF} by inspecting the meaning.
+% The list of parameters is finite so we just use a
+% \cs{cs_if_exist:cTF} test to look up in a table. We abuse
+% \cs{cs_to_str:N}'s ability to remove a leading escape character
+% whatever it is.
+% \begin{macrocode}
+ \prg_new_conditional:Npnn \@@_e_if_toks_register:N #1 { TF }
+ {
+ \token_if_toks_register:NTF #1 { \prg_return_true: }
+ {
+ \cs_if_exist:cTF
+ {
+ @@_e_the_
+ \exp_after:wN \cs_to_str:N
+ \token_to_meaning:N #1
+ :
+ } { \prg_return_true: } { \prg_return_false: }
+ }
+ }
+ \cs_new_eq:NN \@@_e_the_XeTeXinterchartoks: ?
+ \cs_new_eq:NN \@@_e_the_errhelp: ?
+ \cs_new_eq:NN \@@_e_the_everycr: ?
+ \cs_new_eq:NN \@@_e_the_everydisplay: ?
+ \cs_new_eq:NN \@@_e_the_everyeof: ?
+ \cs_new_eq:NN \@@_e_the_everyhbox: ?
+ \cs_new_eq:NN \@@_e_the_everyjob: ?
+ \cs_new_eq:NN \@@_e_the_everymath: ?
+ \cs_new_eq:NN \@@_e_the_everypar: ?
+ \cs_new_eq:NN \@@_e_the_everyvbox: ?
+ \cs_new_eq:NN \@@_e_the_output: ?
+ \cs_new_eq:NN \@@_e_the_pdfpageattr: ?
+ \cs_new_eq:NN \@@_e_the_pdfpageresources: ?
+ \cs_new_eq:NN \@@_e_the_pdfpagesattr: ?
+ \cs_new_eq:NN \@@_e_the_pdfpkmode: ?
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% We are done emulating \texttt{e}-type argument expansion when
+% \tn{expanded} is unavailable.
+% \begin{macrocode}
+ }
+% \end{macrocode}
+%
+%
+% \subsection{Defining function variants}
+%
+% \begin{macrocode}
+%<@@=cs>
+% \end{macrocode}
+%
+% \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:Npx} or \cs{cs_new_protected:Npx}, 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}
+\__kernel_patch:nnNNpn { \__kernel_chk_cs_exist:N #1 } { }
+\cs_new_protected:Npn \cs_generate_variant:Nn #1#2
+ {
+ \@@_generate_variant:N #1
+ \use:x
+ {
+ \@@_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 fist 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{q_mark} is taken as an argument of the |wwNw|
+% auxiliary, and |#3| is \cs{cs_new_protected:Npx}, otherwise it
+% is \cs{cs_new:Npx}.
+% \begin{macrocode}
+\cs_new_protected:Npx \@@_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:Npx
+ \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 }
+ \exp_not:N \q_mark
+ \exp_not:N \q_mark \cs_new_protected:Npx
+ \tl_to_str:n { pr }
+ \exp_not:N \q_mark \cs_new:Npx
+ \exp_not:N \q_stop
+ \exp_not:N \fi:
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_variant:ww
+ #1 { \tl_to_str:n { ma } } #2 \q_mark
+ { \@@_generate_variant:wwNw #1 }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_variant:wwNw
+ #1 { \tl_to_str:n { pr } } #2 \q_mark #3 #4 \q_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
+ \__kernel_msg_error:nnx { 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|, |f|, |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 |x|-expansion. The result is given to
+% \cs{@@_generate_variant:wwNN} (defined later) in the form
+% \meta{processed variant signature} \cs{q_mark} \meta{errors}
+% \cs{q_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:x
+ {
+ \exp_not:N \@@_generate_variant:wwNN
+ \@@_generate_variant_loop:nNwN { }
+ #4
+ \@@_generate_variant_loop_end:nwwwNNnn
+ \q_mark
+ #3 ~
+ { ~ { } \fi: \@@_generate_variant_loop_long:wNNnn } ~
+ { }
+ \q_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 |o|, |V|, |v|, |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 \q_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 \q_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 \q_mark #3 ~ #4 \q_stop #5#6#7#8
+ {
+ \scan_stop: \scan_stop: \fi:
+ \exp_not:N \q_mark
+ \exp_not:N \q_stop
+ \exp_not:N #6
+ \exp_not:c { #7 : #8 #1 #3 }
+ }
+\cs_new:Npn \@@_generate_variant_loop_long:wNNnn #1 \q_stop #2#3#4#5
+ {
+ \exp_not:n
+ {
+ \q_mark
+ \__kernel_msg_error:nnxx { kernel } { variant-too-long }
+ {#5} { \token_to_str:N #3 }
+ \use_none:nnn
+ \q_stop
+ #3
+ #3
+ }
+ }
+\cs_new:Npn \@@_generate_variant_loop_invalid:NNwNNnn
+ #1#2 \fi: \fi: \fi: #3 \q_stop #4#5#6#7
+ {
+ \fi: \fi: \fi:
+ \exp_not:n
+ {
+ \q_mark
+ \__kernel_msg_error:nnxxxx { kernel } { invalid-variant }
+ {#7} { \token_to_str:N #5 } {#1} {#2}
+ \use_none:nnn
+ \q_stop
+ #5
+ #5
+ }
+ }
+\cs_new:Npn \@@_generate_variant_loop_special:NNwNNnn
+ #1#2#3 \q_stop #4#5#6#7
+ {
+ #3 \q_stop #4 #5 {#6} {#7}
+ \exp_not:n
+ {
+ \__kernel_msg_error:nnxxxx
+ { 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 N \else:
+ \if:w p #1 p \else:
+ 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:Npx}. Then define the variant by
+% combining the |\exp_args:N #3| variant and the base function.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn
+ {
+ \cs_if_free:NF #4
+ {
+ \__kernel_debug_log:x
+ {
+ Variant~\token_to_str:N #4~%
+ already~defined;~ not~ changing~ it~ \msg_line_context:
+ }
+ }
+ }
+ { }
+\cs_new_protected:Npn \@@_generate_variant:wwNN
+ #1 \q_mark #2 \q_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}{\@@_generate_internal_variant:wwnw}
+% \begin{macro}[rEXP]{\@@_generate_internal_variant_loop:n}
+% Test if |\exp_args:N #1| is already defined and if not define it via
+% the |\::| commands using the chars in |#1|. If |#1| contains an |x|
+% (this is the place where having converted the original comma-list
+% argument to a string is very important), the result should be
+% protected, and the next variant to be defined using that internal
+% variant should be protected.
+% \begin{macrocode}
+\cs_new_protected:Npx \@@_generate_internal_variant:n #1
+ {
+ \exp_not:N \@@_generate_internal_variant:wwnNwnn
+ #1 \exp_not:N \q_mark
+ { \cs_set_eq:NN \exp_not:N \@@_tmp:w \cs_new_protected:Npx }
+ \cs_new_protected:cpx
+ \token_to_str:N x \exp_not:N \q_mark
+ { }
+ \cs_new:cpx
+ \exp_not:N \q_stop
+ { exp_args:N #1 }
+ {
+ \exp_not:N \@@_generate_internal_variant_loop:n #1
+ { : \exp_not:N \use_i:nn }
+ }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new_protected:Npn \@@_generate_internal_variant:wwnNwnn #1
+ { \token_to_str:N x } #2 \q_mark #3#4#5 \q_stop #6#7
+ {
+ #3
+ \cs_if_free:cT {#6} { #4 {#6} {#7} }
+ }
+% \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}
+% \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:x
+ {
+ \@@_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
+ \__kernel_msg_error:nnx { kernel } { missing-colon }
+ { \token_to_str:c {#1} }
+ \use_i_delimit_by_q_stop:nw
+ \fi:
+ \exp_after:wN \@@_generate_variant:w
+ \tl_to_str:n {#5} , \scan_stop: , \q_recursion_stop
+ \use_none_delimit_by_q_stop:w \q_mark {#1} {#2} {#4} \q_stop
+ }
+\cs_new_protected:Npn \@@_generate_variant:w
+ #1 , #2 \q_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} }
+ {
+ \__kernel_msg_error:nnxx
+ { kernel } { conditional-form-unknown }
+ {#1} { \token_to_str:c { #3 : #4 } }
+ }
+ \fi:
+ \@@_generate_variant:w #2 \q_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, \@@_args_generate:Nn}
+% \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 loop through the variant specifier and
+% convert each letter to \cs[no-index]{::\meta{variant letter}}, with a
+% trailing \cs{:::}.
+% \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}
+ {
+ \__kernel_msg_error:nnnn { kernel } { invalid-exp-args }
+ {####1} {##1}
+ \str_map_break:n { \use_none:nnnn }
+ }
+ }
+ \exp_args:Nc \@@_args_generate:Nn { exp_args:N ##1 } {##1}
+ }
+ }
+\cs_new_protected:Npn \@@_args_generate:Nn #1#2
+ {
+ \cs_if_exist:NF #1
+ {
+ \str_if_in:nnTF {#2} { x } { \cs_new_protected:Npx } { \cs_new:Npx }
+ #1 { \tl_map_function:nN { #2 : } \@@_args_generate:n }
+ }
+ }
+\cs_new:Npn \@@_args_generate:n #1 { \exp_not:c { :: #1 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3file.dtx b/macros/latex/contrib/l3kernel/l3file.dtx
new file mode 100644
index 0000000000..017c9954f7
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3file.dtx
@@ -0,0 +1,2881 @@
+% \iffalse meta-comment
+%
+%% File: l3file.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ File and I/O operations^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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. File names are quoted
+% using |"| tokens if they contain spaces: as a result, |"| tokens are
+% \emph{not} permitted in file names.
+%
+% \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 a \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:cn}
+% \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}[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 = 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 = 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{Writing to files}
+%
+% \begin{function}[updated = 2012-06-05]{\iow_now:Nn, \iow_now:Nx, \iow_now:cn, \iow_now:cx}
+% \begin{syntax}
+% \cs{iow_now:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions 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:x}
+% \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:x}
+% \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:Nx,
+% \iow_shipout:cn, \iow_shipout:cx
+% }
+% \begin{syntax}
+% \cs{iow_shipout:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions writes \meta{tokens} to the specified
+% \meta{stream} when the current page is finalised (\emph{i.e.}~at
+% shipout). The \texttt{x}-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_x: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 = 2012-09-08]
+% {
+% \iow_shipout_x:Nn, \iow_shipout_x:Nx,
+% \iow_shipout_x:cn, \iow_shipout_x:cx
+% }
+% \begin{syntax}
+% \cs{iow_shipout_x:Nn} \meta{stream} \Arg{tokens}
+% \end{syntax}
+% This functions 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:Nx \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_x: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:nxnN}
+% \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_allow_break:} may be used to allow a line-break
+% without inserting a space (this is experimental),
+% \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{x}-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 = 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 operation functions}
+%
+% \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{ext} part for the main (top
+% level) file and that this file always has an empty \meta{dir} component.
+% 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]{\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 should
+% not include the trailing slash. The entries are not expanded when
+% used so may contain active characters but should not feature any
+% variable content. 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}[TF, updated = 2012-02-10]{\file_if_exist:n}
+% \begin{syntax}
+% \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code}
+% \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}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2019-01-16, updated = 2019-02-16]{\file_get:nnN}
+% \begin{syntax}
+% \cs{file_get:nnN} \Arg{filename} \Arg{setup} \meta{tl}
+% \cs{file_get:nnNTF} \Arg{filename} \Arg{setup} \meta{tl} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Defines \meta{tl} to the contents of \meta{filename}.
+% 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.
+% \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 = 2017-06-23, updated = 2017-06-26]
+% {\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}
+% This function does not expand the \meta{full name} before turning it
+% to a string. It assume that the \meta{full name} either contains no
+% quote (|"|) characters or is surrounded by a pair of quotes.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-11, updated = 2019-02-16]
+% {\file_get_mdfive_hash:nN}
+% \begin{syntax}
+% \cs{file_get_mdfive_hash:nN} \Arg{file name} \meta{tl var}
+% \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}.
+% If found, sets the \meta{tl var} to the MD5 sum generated from the
+% content of the file. 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.
+% Where the file is not found, the \meta{tl var} will set to
+% \cs{q_no_value}.
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-07-09, updated = 2019-02-16]
+% {\file_get_size:nN}
+% \begin{syntax}
+% \cs{file_get_size:nN} \Arg{file name} \meta{tl var}
+% \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}.
+% If found, sets the \meta{tl var} to the size of the file in bytes.
+% Where the file is not found, the \meta{tl var} will set to \cs{q_no_value}.
+% 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}
+% \begin{syntax}
+% \cs{file_get_timestamp:nN} \Arg{file name} \meta{tl var}
+% \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}.
+% If found, sets the \meta{tl var} to the modification timestamp of
+% the file in 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}|'|.
+% Where 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}[updated = 2017-06-26]{\file_input:n}
+% \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 = 2014-07-02]{\file_if_exist_input:n, \file_if_exist_input:nF}
+% \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 controlled by
+% \cs{file_path_include:n}. 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}
+%<*initex|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. In format mode, all streams (from $0$ to~$15$) are
+% available, while the package requests streams to \LaTeXe{} as they
+% are needed (initially none are needed), so the starting point
+% varies!
+% \begin{macrocode}
+\seq_new:N \g_@@_streams_seq
+%<*initex>
+\seq_gset_split:Nnn \g_@@_streams_seq { , }
+ { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 }
+%</initex>
+% \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
+%<*package>
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \normalend
+ { \tex_count:D 38 ~ }
+ {
+ \tex_count:D 16 ~ %
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
+ }
+%</package>
+% \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}
+% In package mode, 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}
+%<*package>
+\exp_args:NNf \cs_new_protected:Npn \@@_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newread } }
+\cs_if_exist:NT \normalend
+ {
+ \cs_new_eq:NN \@@_new_aux:N \@@_new:N
+ \cs_set_protected:Npn \@@_new:N #1
+ {
+ \cs_undefine:N #1
+ \@@_new_aux:N #1
+ }
+ }
+%</package>
+% \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, so allocation is simply a question of using the number at
+% the top of the list. In package mode, 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} }
+%<*initex>
+ { \__kernel_msg_fatal:nn { kernel } { input-streams-exhausted } }
+%</initex>
+%<*package>
+ {
+ \@@_new:N #1
+ \tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
+ \@@_open_stream:Nn #1 {#2}
+ }
+%</package>
+ }
+\cs_generate_variant:Nn \__kernel_ior_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_openin:D #1 #2 \scan_stop:
+ }
+% \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_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:nnxxxx }
+\cs_new_protected:Npn \ior_log_list: { \@@_list:N \msg_log:nnxxxx }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ #1 { LaTeX / 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 }
+ {
+ \cs_if_exist:NTF #1
+ {
+ \int_compare:nTF { -1 < #1 < \c_@@_term_ior }
+ {
+ \if_eof:w #1
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ { \prg_return_true: }
+ }
+ { \prg_return_true: }
+ }
+% \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 to 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}{\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. The stream $18$ is special, as \tn{write}18 is used to
+% denote commands to be sent to the OS\@.
+% \begin{macrocode}
+\seq_new:N \g_@@_streams_seq
+%<*initex>
+\exp_args:Nnx \use:n
+ { \seq_gset_split:Nnn \g_@@_streams_seq { } }
+ {
+ \int_step_function:nnN { 0 } { \c_term_iow }
+ \prg_do_nothing:
+ }
+\int_compare:nNnF \c_term_iow < { 18 }
+ { \seq_gremove_all:Nn \g_@@_streams_seq { 18 } }
+%</initex>
+% \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
+%<*package>
+\int_step_inline:nnn
+ { 0 }
+ {
+ \cs_if_exist:NTF \normalend
+ { \tex_count:D 39 ~ }
+ {
+ \tex_count:D 17 ~
+ \cs_if_exist:NT \loccount { - 1 }
+ }
+ }
+ {
+ \prop_gput:Nnn \g_@@_streams_prop {#1} { Reserved~by~format }
+ }
+%</package>
+% \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} in package mode, making sure
+% that it is not \tn{outer}.
+% \begin{macrocode}
+%<*package>
+\exp_args:NNf \cs_new_protected:Npn \@@_new:N
+ { \exp_args:NNc \exp_after:wN \exp_stop_f: { newwrite } }
+%</package>
+% \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:cn}
+% \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_file_name_sanitize:nN {#2} \l_@@_file_name_tl
+ \iow_close:N #1
+ \seq_gpop:NNTF \g_@@_streams_seq \l_@@_stream_tl
+ { \@@_open_stream:NV #1 \l_@@_file_name_tl }
+%<*initex>
+ { \__kernel_msg_fatal:nn { kernel } { output-streams-exhausted } }
+%</initex>
+%<*package>
+ {
+ \@@_new:N #1
+ \tl_set:Nx \l_@@_stream_tl { \int_eval:n {#1} }
+ \@@_open_stream:NV #1 \l_@@_file_name_tl
+ }
+%</package>
+ }
+\cs_generate_variant:Nn \iow_open:Nn { c }
+\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 #2 \scan_stop:
+ }
+\cs_generate_variant:Nn \@@_open_stream:Nn { NV }
+% \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_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:nnxxxx }
+\cs_new_protected:Npn \iow_log_list: { \@@_list:N \msg_log:nnxxxx }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ #1 { LaTeX / 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_x:Nn, \iow_shipout_x:Nx, \iow_shipout_x:cn, \iow_shipout_x:cx}
+% First the easy part, this is the primitive, which expects its
+% argument to be braced.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_shipout_x:Nn #1#2
+ { \tex_write:D #1 {#2} }
+\cs_generate_variant:Nn \iow_shipout_x:Nn { c, Nx, cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_shipout:Nn, \iow_shipout:Nx, \iow_shipout:cn, \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 { c, Nx, cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Immediate writing}
+%
+% \begin{macro}{\__kernel_iow_with:Nnn}
+% \begin{macro}{\@@_with:nNnn}
+% 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 }
+ { \exp_args:No \@@_with:nNnn { \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}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\iow_now:Nn, \iow_now:Nx, \iow_now:cn, \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 { c, Nx, cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\iow_log:n, \iow_log:x}
+% \begin{macro}{\iow_term:n, \iow_term:x}
+% Writing to the log and the terminal directly are relatively easy.
+% \begin{macrocode}
+\cs_set_protected:Npn \iow_log:x { \iow_now:Nx \c_log_iow }
+\cs_new_protected:Npn \iow_log:n { \iow_now:Nn \c_log_iow }
+\cs_set_protected:Npn \iow_term:x { \iow_now:Nx \c_term_iow }
+\cs_new_protected:Npn \iow_term:n { \iow_now:Nn \c_term_iow }
+% \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
+ {
+ \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:Nx \@@_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:Nx \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:cx { c_@@_wrap_ #1 _marker_tl }
+ {
+ \c_@@_wrap_marker_tl
+ #1
+ \c_catcode_other_space_tl
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}{\iow_allow_break:}
+% \begin{macro}[EXP]{\@@_allow_break:}
+% \begin{macro}[EXP]{\@@_allow_break_error:}
+% We set \cs{iow_allow_break:n} to produce an error when outside
+% messages. Within wrapped message, it is set to \cs{@@_allow_break:}
+% when valid and otherwise to \cs{@@_allow_break_error:}. The second
+% produces an error expandably.
+% \begin{macrocode}
+\cs_new_protected:Npn \iow_allow_break:
+ {
+ \__kernel_msg_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_allow_break: }
+ }
+\cs_new:Npx \@@_allow_break: { \c_@@_wrap_allow_break_marker_tl }
+\cs_new:Npn \@@_allow_break_error:
+ {
+ \__kernel_msg_expandable_error:nnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_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
+ {
+ \__kernel_msg_error:nnnnn { kernel } { iow-indent }
+ { \iow_wrap:nnnN } { \iow_indent:n } {#1}
+ #1
+ }
+\cs_new:Npx \@@_indent:n #1
+ {
+ \c_@@_wrap_indent_marker_tl
+ #1
+ \c_@@_wrap_unindent_marker_tl
+ }
+\cs_new:Npn \@@_indent_error:n #1
+ {
+ \__kernel_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:nxnN}
+% 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:
+%<package> \cs_if_exist_use:N \conditionally@traceoff
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \cs_set:Npx \{ { \token_to_str:N \{ }
+ \cs_set:Npx \# { \token_to_str:N \# }
+ \cs_set:Npx \} { \token_to_str:N \} }
+ \cs_set:Npx \% { \token_to_str:N \% }
+ \cs_set:Npx \~ { \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_allow_break: \@@_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}
+%<package> \cs_set_eq:NN \protect \token_to_str:N
+ \tl_set:Nx \l_@@_wrap_tl {#1}
+ \cs_set_eq:NN \iow_allow_break: \@@_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}
+ \tl_set:Nx \l_@@_newline_tl { \iow_newline: #2 }
+ \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 { nx }
+% \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:
+ {
+ \tl_set:Nx \l_@@_wrap_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n \l_@@_wrap_tl
+ \c_@@_wrap_end_marker_tl
+ }
+ \tl_set:Nx \l_@@_wrap_tl
+ {
+ \exp_after:wN \@@_wrap_fix_newline:w \l_@@_wrap_tl
+ ^^J \q_nil ^^J \q_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_q_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 \q_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{intexpr} |;|. It stores the
+% \meta{string} and up to \meta{intexpr} 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 \q_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 }
+ \q_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 \q_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 \q_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 \q_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 \q_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
+ {
+ \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:Nx \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:Nx \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 }
+ \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
+ {
+ \tl_set:Nx \l_@@_line_tl
+ { \l_@@_line_tl \l_@@_line_part_tl }
+ \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 \q_mark #1 \q_mark \q_stop }
+ \cs_new:Npn \@@_wrap_trim:w ##1 #1 \q_mark
+ { \@@_wrap_trim_aux:w ##1 \q_mark }
+ \cs_new:Npn \@@_wrap_trim_aux:w ##1 \q_mark ##2 \q_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_@@_internal_ior}
+% A reserved stream to test for file existence.
+% \begin{macrocode}
+\ior_new:N \g_@@_internal_ior
+% \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.
+% For the format the file name needs to be picked up at the start of the
+% run. In \LaTeXe{} package mode the current file name is collected from
+% \tn{@currname}.
+% \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
+%<*initex>
+\tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \str_gset:Nx \g_file_curr_name_str { \tex_jobname:D }
+ }
+%</initex>
+%<*package>
+\cs_if_exist:NT \@currname
+ { \str_gset_eq:NN \g_file_curr_name_str \@currname }
+%</package>
+% \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.
+% \begin{macrocode}
+\seq_new:N \g_@@_stack_seq
+%<*package>
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \cs_set:Npn \@@_tmp:w ##1 " ##2 " ##3 \q_stop
+ { { } {##2} { } }
+ \seq_gput_right:Nx \g_@@_stack_seq
+ {
+ \exp_after:wN \@@_tmp:w \tex_jobname:D
+ " \tex_jobname:D " \q_stop
+ }
+ }
+ {
+ \seq_gput_right:Nn \g_@@_stack_seq { { } {#1} {#2} }
+ \@@_tmp:w
+ }
+ }
+ \cs_if_exist:NT \@currnamestack
+ { \exp_after:wN \@@_tmp:w \@currnamestack }
+\group_end:
+%</package>
+% \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! In format mode, this
+% is done at the very start of the \TeX{} run. In package mode we
+% will eventually copy the contents of \cs{@filelist}.
+% \begin{macrocode}
+\seq_new:N \g_@@_record_seq
+%<*initex>
+\tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \seq_gput_right:NV \g_@@_record_seq \g_file_curr_name_str
+ }
+%</initex>
+% \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 in package mode.
+% \begin{macrocode}
+%<*package>
+\seq_new:N \l_@@_tmp_seq
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\__kernel_file_name_sanitize:nN}
+% \begin{macro}{\@@_name_quote:nN}
+% \begin{macro}{\@@_name_sanitize_aux:n}
+% For converting a token list to a string where active characters are treated
+% as strings from the start. The logic to the quoting normalisation is the
+% same as used by \texttt{lualatexquotejobname}: check for balanced |"|, and
+% assuming they balance strip all of them out before quoting the entire name
+% if it contains spaces.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_file_name_sanitize:nN #1#2
+ {
+ \group_begin:
+ \seq_map_inline:Nn \l_char_active_seq
+ {
+ \tl_set:Nx \l_@@_internal_tl { \iow_char:N ##1 }
+ \char_set_active_eq:NN ##1 \l_@@_internal_tl
+ }
+ \tl_set:Nx \l_@@_internal_tl {#1}
+ \exp_args:NNNV \group_end:
+ \str_set:Nn #2 \l_@@_internal_tl
+ }
+\cs_new_protected:Npn \@@_name_quote:nN #1#2
+ {
+ \str_set:Nx #2 {#1}
+ \int_if_even:nF
+ { 0 \tl_map_function:NN #2 \@@_name_quote_aux:n }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { unbalanced-quote-in-filename } {#2}
+ }
+ \tl_remove_all:Nn #2 { " }
+ \tl_if_in:NnT #2 { ~ }
+ { \str_set:Nx #2 { " \exp_not:V #2 " } }
+ }
+\cs_new:Npn \@@_name_quote_aux:n #1
+ { \token_if_eq_charcode:NNT #1 " { + 1 } }
+% \end{macrocode}
+% \end{macro}
+% \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:Nx \c_@@_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\file_get:nnN}
+% \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 } }
+ }
+\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: }
+ }
+\cs_new_protected:Npn \@@_get_aux:nnN #1#2#3
+ {
+ \if_false: { \fi:
+ \group_begin:
+ \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:
+ }
+\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}{\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}
+% The way to test if a file exists is to try to open it: if it does
+% not exist then \TeX{} reports end-of-file. A search is made
+% looking at each potential path in turn (starting from the current
+% directory). The first location is of course treated as the correct
+% one: this is done by jumping to \cs{prg_break_point:}. If nothing
+% is found, |#2| is returned empty. A special case when there is no
+% extension is that once the first location is found we test the
+% existence of the file with |.tex| extension in that directory, and
+% if it exists we include the |.tex| extension in the result.
+% \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_file_name_sanitize:nN {#1} \l_@@_base_name_tl
+ \@@_get_full_name_search:nN { } \use:n
+ \seq_map_inline:Nn \l_file_search_path_seq
+ { \@@_get_full_name_search:nN { ##1 / } \seq_map_break:n }
+%<*package>
+ \cs_if_exist:NT \input@path
+ {
+ \tl_map_inline:Nn \input@path
+ { \@@_get_full_name_search:nN { ##1 } \tl_map_break:n }
+ }
+%</package>
+ \tl_set:Nn \l_@@_full_name_tl { \q_no_value }
+ \prg_break_point:
+ \quark_if_no_value:NTF \l_@@_full_name_tl
+ {
+ \ior_close:N \g_@@_internal_ior
+ \prg_return_false:
+ }
+ {
+ \file_parse_full_name:VNNN \l_@@_full_name_tl
+ \l_@@_dir_str \l_@@_name_str \l_@@_ext_str
+ \str_if_empty:NT \l_@@_ext_str
+ {
+ \__kernel_ior_open:No \g_@@_internal_ior
+ { \l_@@_full_name_tl .tex }
+ \ior_if_eof:NF \g_@@_internal_ior
+ { \tl_put_right:Nn \l_@@_full_name_tl { .tex } }
+ }
+ \ior_close:N \g_@@_internal_ior
+ \tl_set_eq:NN #2 \l_@@_full_name_tl
+ \prg_return_true:
+ }
+ }
+\cs_generate_variant:Nn \file_get_full_name:nNT { V }
+\cs_generate_variant:Nn \file_get_full_name:nNF { V }
+\cs_generate_variant:Nn \file_get_full_name:nNTF { V }
+\cs_new_protected:Npn \@@_get_full_name_search:nN #1#2
+ {
+ \@@_name_quote:nN
+ { \tl_to_str:n {#1} \l_@@_base_name_tl }
+ \l_@@_full_name_tl
+ \__kernel_ior_open:No \g_@@_internal_ior \l_@@_full_name_tl
+ \ior_if_eof:NF \g_@@_internal_ior { #2 { \prg_break: } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\file_get_mdfive_hash:nN, \file_get_size:nN, \file_get_timestamp:nN}
+% \begin{macro}{\@@_get_details:nnN}
+% These are all wrappers around the \pdfTeX{} primitives doing the same
+% jobs: as we want consistent file paths to be found, they are all set up
+% using \cs{file_get_full_name:nN} and so are non-expandable \texttt{get}
+% functions. Much of the code is repetitive but we need to branch for
+% \LuaTeX{} (emulation in Lua), for the slightly different syntax
+% needed for \tn{tex_mdfivesum:D} and for the fact that primitive
+% coverage varies in other engines.
+% \begin{macrocode}
+\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_new_protected:Npn \file_get_size:nN #1#2
+ { \file_get_size:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\cs_new_protected:Npn \file_get_timestamp:nN #1#2
+ { \file_get_timestamp:nNF {#1} #2 { \tl_set:Nn #2 { \q_no_value } } }
+\prg_new_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { mdfivesum } {#2} }
+\prg_new_protected_conditional:Npnn \file_get_size:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { size } {#2} }
+\prg_new_protected_conditional:Npnn \file_get_timestamp:nN #1#2 { T , F , TF }
+ { \@@_get_details:nnN {#1} { moddate } {#2} }
+\cs_new_protected:Npn \@@_get_details:nnN #1#2#3
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ {
+ \tl_set:Nx #3
+ {
+ \use:c { tex_file #2 :D } \exp_after:wN
+ { \l_@@_full_name_tl }
+ }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_set_protected:Npn \@@_get_details:nnN #1#2#3
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ {
+ \tl_set:Nx #3
+ {
+ \lua_now:e
+ {
+ l3kernel.file#2
+ ( " \lua_escape:e { \l_@@_full_name_tl } " )
+ }
+ }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+ }
+ {
+ \prg_set_protected_conditional:Npnn \file_get_mdfive_hash:nN #1#2
+ { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ {
+ \tl_set:Nx #2
+ {
+ \tex_mdfivesum:D file \exp_after:wN
+ { \l_@@_full_name_tl }
+ }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+ \cs_if_exist:NF \tex_filesize:D
+ {
+ \cs_set_protected:Npn \@@_get_details:nnN #1#2#3
+ {
+ \tl_clear:N #3
+ \__kernel_msg_error:nnx
+ { kernel } { primitive-not-available }
+ { \exp_not:c { (pdf)file #2 } }
+ \prg_return_false:
+ }
+ }
+ }
+\__kernel_msg_new:nnnn { kernel } { primitive-not-available }
+ { Primitive~\token_to_str:N #1 not~available }
+ {
+ The~version~of~XeTeX~in~use~does~not~provide~functionality~equivalent~to~
+ the~\token_to_str:N #1 primitive.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[TF]{\file_if_exist:n}
+% 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_protected_conditional:Npnn \file_if_exist:n #1 { T , F , TF }
+ {
+ \file_get_full_name:nNTF {#1} \l_@@_full_name_tl
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_if_exist_input:n, \file_if_exist_input:nF}
+% 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_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}
+ }
+% \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
+ {
+ \__kernel_file_name_sanitize:nN {#1} \l_@@_base_name_tl
+ \__kernel_msg_error:nnx { kernel } { file-not-found }
+ { \l_@@_base_name_tl }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\file_input:n}
+% \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_new_protected:Npn \@@_input:n #1
+ {
+%<*initex>
+ \seq_gput_right:Nn \g_@@_record_seq {#1}
+%</initex>
+%<*package>
+ \clist_if_exist:NTF \@filelist
+ { \@addtofilelist {#1} }
+ { \seq_gput_right:Nn \g_@@_record_seq {#1} }
+%</package>
+ \@@_input_push:n {#1}
+ \tex_input:D #1 \c_space_tl
+ \@@_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:Nx \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
+ }
+%<*package>
+\cs_new_eq:NN \__kernel_file_input_push:n \__file_input_push:n
+%</package>
+\cs_new_protected:Npn \@@_input_pop:
+ {
+ \seq_gpop:NN \g_@@_stack_seq \l_@@_internal_tl
+ \exp_after:wN \@@_input_pop:nnn \l_@@_internal_tl
+ }
+%<*package>
+\cs_new_eq:NN \__kernel_file_input_pop: \__file_input_pop:
+%</package>
+\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_parse_full_name:nNNN, \file_parse_full_name:VNNN}
+% \begin{macro}
+% {\@@_parse_full_name_auxi:w, \@@_parse_full_name_split:nNNNTF}
+% Parsing starts by stripping off any surrounding quotes. Then find
+% the directory |#4| by splitting at the last~|/|. (The auxiliary
+% returns \texttt{true}/\texttt{false} depending on whether it found
+% the delimiter.) We correct for the case of a file in the root |/|,
+% as in that case we wish to keep the trailing (and only) slash. Then
+% split the base name |#5| at the last dot. If there was indeed a
+% dot, |#5| contains the name and |#6| the extension without the dot,
+% which we add back for convenience. In the special case of no
+% extension given, the auxiliary stored the name into |#6|, we just
+% have to move it to |#5|.
+% \begin{macrocode}
+\cs_new_protected:Npn \file_parse_full_name:nNNN #1#2#3#4
+ {
+ \exp_after:wN \@@_parse_full_name_auxi:w
+ \tl_to_str:n { #1 " #1 " } \q_stop #2#3#4
+ }
+\cs_generate_variant:Nn \file_parse_full_name:nNNN { V }
+\cs_new_protected:Npn \@@_parse_full_name_auxi:w
+ #1 " #2 " #3 \q_stop #4#5#6
+ {
+ \@@_parse_full_name_split:nNNNTF {#2} / #4 #5
+ { \str_if_empty:NT #4 { \str_set:Nn #4 { / } } }
+ { }
+ \exp_args:No \@@_parse_full_name_split:nNNNTF {#5} . #5 #6
+ { \str_put_left:Nn #6 { . } }
+ {
+ \str_set_eq:NN #5 #6
+ \str_clear:N #6
+ }
+ }
+\cs_new_protected:Npn \@@_parse_full_name_split:nNNNTF #1#2#3#4
+ {
+ \cs_set_protected:Npn \@@_tmp:w ##1 ##2 #2 ##3 \q_stop
+ {
+ \tl_if_empty:nTF {##3}
+ {
+ \str_set:Nn #4 {##2}
+ \tl_if_empty:nTF {##1}
+ {
+ \str_clear:N #3
+ \use_ii:nn
+ }
+ {
+ \str_set:Nx #3 { \str_tail:n {##1} }
+ \use_i:nn
+ }
+ }
+ { \@@_tmp:w { ##1 #2 ##2 } ##3 \q_stop }
+ }
+ \@@_tmp:w { } #1 #2 \q_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \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:nnxxxx }
+\cs_new_protected:Npn \file_log_list: { \@@_list:N \msg_log:nnxxxx }
+\cs_new_protected:Npn \@@_list:N #1
+ {
+ \seq_clear:N \l_@@_tmp_seq
+%<*package>
+ \clist_if_exist:NT \@filelist
+ {
+ \exp_args:NNx \seq_set_from_clist:Nn \l_@@_tmp_seq
+ { \tl_to_str:N \@filelist }
+ }
+%</package>
+ \seq_concat:NNN \l_@@_tmp_seq \l_@@_tmp_seq \g_@@_record_seq
+ \seq_remove_duplicates:N \l_@@_tmp_seq
+ #1 { LaTeX/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}
+%<*package>
+\AtBeginDocument
+ {
+ \exp_args:NNx \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
+ }
+%</package>
+% \end{macrocode}
+%
+% \subsection{GetIfInfo}
+%
+% \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
+ {
+ \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 ~ \q_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 \q_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 - \q_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 \q_stop
+ { \tl_set:Nn \ExplFileDate { #1/#2/#3 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\__kernel_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.
+ }
+\__kernel_msg_new:nnn { kernel } { file-list }
+ {
+ >~File~List~<
+ #1 \\
+ .............
+ }
+\__kernel_msg_new:nnnn { kernel } { input-streams-exhausted }
+ { Input~streams~exhausted }
+ {
+ TeX~can~only~open~up~to~16~input~streams~at~one~time.\\
+ All~16~are~currently~in~use,~and~something~wanted~to~open~
+ another~one.
+ }
+\__kernel_msg_new:nnnn { kernel } { output-streams-exhausted }
+ { Output~streams~exhausted }
+ {
+ TeX~can~only~open~up~to~16~output~streams~at~one~time.\\
+ All~16~are~currently~in~use,~and~something~wanted~to~open~
+ another~one.
+ }
+\__kernel_msg_new:nnnn { kernel } { unbalanced-quote-in-filename }
+ { Unbalanced~quotes~in~file~name~'#1'. }
+ {
+ File~names~must~contain~balanced~numbers~of~quotes~(").
+ }
+\__kernel_msg_new:nnnn { kernel } { iow-indent }
+ { Only~#1 (arg~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}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3final.dtx b/macros/latex/contrib/l3kernel/l3final.dtx
new file mode 100644
index 0000000000..13ad327dbc
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3final.dtx
@@ -0,0 +1,389 @@
+% \iffalse meta-comment
+%
+%% File: l3final.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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{l3final} package\\ Format finalisation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module is the end of the \LaTeX3 format file. Currently, a lot of this
+% is copy-pasted from the \LaTeXe{} format or is highly unstable (essentially
+% hacks which need revisiting later).
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3final} Implementation}
+%
+% \begin{macrocode}
+%<*initex>
+% \end{macrocode}
+%
+% \subsection{Input encoding}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% The letters |a|--|z| and |A|--|Z| have the correct catcode directly from \IniTeX{}
+% while for Unicode engines (almost) all characters to be treated as letters
+% are defined by the automatic data parsing. Thus the changes here are to
+% deal with the additional cases.
+%
+% All the characters in the range $0$--$31$ \emph{except} tab (|^^I|), nl
+% (|^^J|), ff (|^^L|) and cr (|^^M|).
+% \begin{macrocode}
+\int_step_inline:nnn { 0 } { `\^^H }
+ { \char_set_catcode_invalid:n {#1} }
+\char_set_catcode_invalid:n { `\^^K }
+\int_step_inline:nnn { `\^^N } { 31 }
+ { \char_set_catcode_invalid:n {#1} }
+% \end{macrocode}
+% The same is true for the top of the $7$-bit range.
+% \begin{macrocode}
+\char_set_catcode_invalid:n { 127 }
+% \end{macrocode}
+% For the $8$-bit engines dotless-I and dotless-J need to be valid,
+% and these therefore appear in some following code. To avoid an issue
+% when those lines are read, the chars are made valid here and that is
+% reversed for Unicode engines below.
+% \begin{macrocode}
+\char_set_catcode_letter:n { `\^^Y }
+\char_set_catcode_letter:n { `\^^Z }
+% \end{macrocode}
+%
+% For non-Unicode engine we now need to convert from UTF-$8$ to $8$-bit
+% for pattern reading and document use. The set up here is copied from
+% the file |conv-utf8-ec.tex| maintained by \textsc{tug} for hyphenation
+% pattern use. As some of the relevant primitives have been moved and to
+% allow for self-contained code that is copied here with minor adjustments.
+% (The primitives have to be available at point of use not just at point of
+% definition so a compatibility layer is hard to arrange here.)
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+% \end{macrocode}
+% Unicode engines: make these two invalid (this happens after \TeX{}
+% has read and thrown away their use in the following).
+% \begin{macrocode}
+ {
+ \char_set_catcode_invalid:n { `\^^Y }
+ \char_set_catcode_invalid:n { `\^^Z }
+ }
+% \end{macrocode}
+% Now for $8$-bit engines.
+% \begin{macrocode}
+ {
+% \end{macrocode}
+% At least for the present, make \upTeX{} behave like \pdfTeX{} so
+% the set up is easier.
+% \begin{macrocode}
+ \sys_if_engine_uptex:T
+ { \tex_disablecjktoken:D }
+% \end{macrocode}
+% The actual mappings: these are kept low-level for performance reasons.
+% \begin{macrocode}
+ \cs_new:cpn { @@_active_C3:N } #1
+ {
+ \if_meaning:w #1 ^^9f ^^ff \else: % ß - U+00DF - germandbls
+ \if_meaning:w #1 ^^a0 ^^e0 \else: % à - U+00E0 - agrave
+ \if_meaning:w #1 ^^a1 ^^e1 \else: % á - U+00E1 - aacute
+ \if_meaning:w #1 ^^a2 ^^e2 \else: % â - U+00E2 - acircumflex
+ \if_meaning:w #1 ^^a3 ^^e3 \else: % ã - U+00E3 - atilde
+ \if_meaning:w #1 ^^a4 ^^e4 \else: % ä - U+00E4 - adieresis
+ \if_meaning:w #1 ^^a5 ^^e5 \else: % å - U+00E5 - aring
+ \if_meaning:w #1 ^^a6 ^^e6 \else: % æ - U+00E6 - ae
+ \if_meaning:w #1 ^^a7 ^^e7 \else: % ç - U+00E7 - ccedilla
+ \if_meaning:w #1 ^^a8 ^^e8 \else: % è - U+00E8 - egrave
+ \if_meaning:w #1 ^^a9 ^^e9 \else: % é - U+00E9 - eacute
+ \if_meaning:w #1 ^^aa ^^ea \else: % ê - U+00EA - ecircumflex
+ \if_meaning:w #1 ^^ab ^^eb \else: % ë - U+00EB - edieresis
+ \if_meaning:w #1 ^^ac ^^ec \else: % ì - U+00EC - igrave
+ \if_meaning:w #1 ^^ad ^^ed \else: % í - U+00ED - iacute
+ \if_meaning:w #1 ^^ae ^^ee \else: % î - U+00EE - icircumflex
+ \if_meaning:w #1 ^^af ^^ef \else: % ï - U+00EF - idieresis
+ \if_meaning:w #1 ^^b0 ^^f0 \else: % ð - U+00F0 - eth
+ \if_meaning:w #1 ^^b1 ^^f1 \else: % ñ - U+00F1 - ntilde
+ \if_meaning:w #1 ^^b2 ^^f2 \else: % ò - U+00F2 - ograve
+ \if_meaning:w #1 ^^b3 ^^f3 \else: % ó - U+00F3 - oacute
+ \if_meaning:w #1 ^^b4 ^^f4 \else: % ô - U+00F4 - ocircumflex
+ \if_meaning:w #1 ^^b5 ^^f5 \else: % õ - U+00F5 - otilde
+ \if_meaning:w #1 ^^b6 ^^f6 \else: % ö - U+00F6 - odieresis
+ \if_meaning:w #1 ^^b8 ^^f8 \else: % ø - U+00F8 - oslash
+ \if_meaning:w #1 ^^b9 ^^f9 \else: % ù - U+00F9 - ugrave
+ \if_meaning:w #1 ^^ba ^^fa \else: % ú - U+00FA - uacute
+ \if_meaning:w #1 ^^bb ^^fb \else: % û - U+00FB - ucircumflex
+ \if_meaning:w #1 ^^bc ^^fc \else: % ü - U+00FC - udieresis
+ \if_meaning:w #1 ^^bd ^^fd \else: % ý - U+00FD - yacute
+ \if_meaning:w #1 ^^be ^^fe \else: % þ - U+00FE - thorn
+ \if_meaning:w #1 ^^bf ^^b8 \else: % ÿ - U+00FF - ydieresis
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ }
+ \cs_new:cpn { @@_active_C4:N } #1
+ {
+ \if_meaning:w #1 ^^83 ^^a0 \else: % ă - U+0103 - abreve
+ \if_meaning:w #1 ^^85 ^^a1 \else: % ą - U+0105 - aogonek
+ \if_meaning:w #1 ^^87 ^^a2 \else: % ć - U+0107 - cacute
+ \if_meaning:w #1 ^^8d ^^a3 \else: % č - U+010D - ccaron
+ \if_meaning:w #1 ^^8f ^^a4 \else: % ď - U+010F - dcaron
+ \if_meaning:w #1 ^^91 ^^9e \else: % đ - U+0111 - dcroat
+ \if_meaning:w #1 ^^99 ^^a6 \else: % ę - U+0119 - eogonek
+ \if_meaning:w #1 ^^9b ^^a5 \else: % ě - U+011B - ecaron
+ \if_meaning:w #1 ^^9f ^^a7 \else: % ğ - U+011F - gbreve
+ \if_meaning:w #1 ^^b1 ^^19 \else: % ı - U+0131 - dotlessi
+ \if_meaning:w #1 ^^b3 ^^bc \else: % ij - U+0133 - ij
+ \if_meaning:w #1 ^^ba ^^a8 \else: % ĺ - U+013A - lacute
+ \if_meaning:w #1 ^^be ^^a9 \else: % ľ - U+013E - lcaron
+ \__kernel_msg_expandable_error:nn { kernel } { encoding-failure }
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ }
+ \cs_new:cpn { @@_active_C5:N } #1
+ {
+ \if_meaning:w #1 ^^82 ^^aa \else: % ł - U+0142 - lslash
+ \if_meaning:w #1 ^^84 ^^ab \else: % ń - U+0144 - nacute
+ \if_meaning:w #1 ^^88 ^^ac \else: % ň - U+0148 - ncaron
+ \if_meaning:w #1 ^^8b ^^ad \else: % ŋ - U+014B - eng
+ \if_meaning:w #1 ^^91 ^^ae \else: % ő - U+0151 - ohungarumlaut
+ \if_meaning:w #1 ^^93 ^^f7 \else: % œ - U+0153 - oe
+ \if_meaning:w #1 ^^95 ^^af \else: % ŕ - U+0155 - racute
+ \if_meaning:w #1 ^^99 ^^b0 \else: % ř - U+0159 - rcaron
+ \if_meaning:w #1 ^^9b ^^b1 \else: % ś - U+015B - sacute
+ \if_meaning:w #1 ^^9f ^^b3 \else: % ş - U+015F - scedilla
+ \if_meaning:w #1 ^^a1 ^^b2 \else: % š - U+0161 - scaron
+ \if_meaning:w #1 ^^a5 ^^b4 \else: % ť - U+0165 - tcaron
+ \if_meaning:w #1 ^^af ^^b7 \else: % ů - U+016F - uring
+ \if_meaning:w #1 ^^b1 ^^b6 \else: % ű - U+0171 - uhungarumlaut
+ \if_meaning:w #1 ^^ba ^^b9 \else: % ź - U+017A - zacute
+ \if_meaning:w #1 ^^bc ^^bb \else: % ż - U+017C - zdotaccent
+ \if_meaning:w #1 ^^be ^^ba \else: % ž - U+017E - zcaron
+ \__kernel_msg_expandable_error:nn { kernel } { encoding-failure }
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi:
+ }
+ \cs_new:cpn { @@_active_C8:N } #1
+ {
+ \if_meaning:w #1 ^^99 ^^b3 \else: % ș - U+0219 - scommaaccent
+ \if_meaning:w #1 ^^9b ^^b5 \else: % ț - U+021B - tcommaaccent
+ \if_meaning:w #1 ^^b7 ^^1a \else: % ȷ - U+0237 - dotlessj
+ \__kernel_msg_expandable_error:nn { kernel } { encoding-failure }
+ \fi: \fi: \fi:
+ }
+% \end{macrocode}
+% Install and record the active characters.
+% \begin{macrocode}
+ \clist_map_inline:nn { C3 , C4 , C5 , C8 }
+ {
+ \char_set_catcode_active:n { "#1 }
+ \char_set_active_eq:nc { "#1 } { @@_active_ #1 :N }
+ \seq_put_right:Nx \l_char_special_seq
+ { \exp_not:c { \char_generate:nn { "#1 } { 12 } } }
+ \seq_put_right:Nx \l_char_active_seq
+ { \exp_not:c { \char_generate:nn { "#1 } { 12 } } }
+ }
+ \__kernel_msg_new:nnn { kernel } { encoding-failure }
+ { Unknown~UTF-8~char }
+% \end{macrocode}
+% All of the chars are lower case so give them the correct \tn{lccode}
+% values.
+% \begin{macrocode}
+ \clist_map_inline:nn
+ {
+ 19 , 1A , 9E , A0 , A1 , A2 , A3 , A4 , A5 , A6 , A7 , A8 , A9 ,
+ AA , AB , AC , AD , AE , AF , B0 , B1 , B2 , B3 , B3 , B4 , B5 ,
+ B6 , B7 , B8 , B9 , BA , BB , BC , E0 , E1 , E2 , E3 , E4 , E5 ,
+ E6 , E7 , E8 , E9 , EA , EB , EC , ED , EE , EF , F0 , F1 , F2 ,
+ F3 , F4 , F5 , F6 , F7 , F8 , F9 , FA , FB , FC , FD , FE , FF
+ }
+ { \char_set_lccode:nn { "#1 } { "#1 } }
+ }
+% \end{macrocode}
+%
+% \subsection{Temporary hacks}
+%
+% \begin{macro}{\T1/lmr/m/n/10, \TU/lmr/m/n/10}
+% For \emph{testing only} provide some kind of output: for that we
+% need a font. At present, select Latin Modern Roman at 10\,pt:
+% entirely arbitrary but at least usable.
+% \begin{macrocode}
+\sys_if_engine_luatex:T
+ {
+ \tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \lua_now:n { require("l3format.lua") }
+ }
+ }
+\use:x
+ {
+ \tex_everyjob:D
+ {
+ \tex_the:D \tex_everyjob:D
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \tex_font:D \exp_not:c { TU/lmr/m/n/10 }
+ = "[lmroman10-regular.otf]/OT" \scan_stop:
+ \exp_not:c { TU/lmr/m/n/10 }
+ \tex_font:D \exp_not:c { TU/lmm/m/n/10 }
+ = "[latinmodern-math.otf]/OT:mode=base;script=math;" \scan_stop:
+ \tex_font:D \exp_not:c { TU/lmm/m/n/7 }
+ = "[latinmodern-math.otf]/OT:mode=base;script=math;+ssty=0;"~at~7pt \scan_stop:
+ \tex_font:D \exp_not:c { TU/lmm/m/n/5 }
+ = "[latinmodern-math.otf]/OT:mode=base;script=math;+ssty=1;"~at~5pt \scan_stop:
+ \exp_not:N \int_step_inline:nnn { 0 } { 3 }
+ {
+ \tex_textfont:D ##1 = \exp_not:c { TU/lmm/m/n/10 }
+ \tex_scriptfont:D ##1 = \exp_not:c { TU/lmm/m/n/7 }
+ \tex_scriptscriptfont:D ##1 = \exp_not:c { TU/lmm/m/n/5 }
+ }
+ }
+ {
+ \tex_font:D \exp_not:c { T1/lmr/m/n/10 }
+ = ec-lmr10 \scan_stop:
+ \exp_not:c { T1/lmr/m/n/10 }
+ \tex_font:D \exp_not:c { OT1/lmr/m/n/10 }
+ = rm-lmr10 \scan_stop:
+ \tex_font:D \exp_not:c { OML/lmm/m/it/10 }
+ = lmmi10 \scan_stop:
+ \tex_font:D \exp_not:c { OMS/lmsy/m/n/10 }
+ = lmsy10 \scan_stop:
+ \tex_font:D \exp_not:c { OMX/lmex/m/n/10 }
+ = lmex10 \scan_stop:
+ \tex_font:D \exp_not:c { OT1/lmr/m/n/7 }
+ = rm-lmr7 \scan_stop:
+ \tex_font:D \exp_not:c { OML/lmm/m/it/7 }
+ = lmmi7 \scan_stop:
+ \tex_font:D \exp_not:c { OMS/lmsy/m/n/7 }
+ = lmsy7 \scan_stop:
+ \tex_font:D \exp_not:c { OMX/lmex/m/n/7 }
+ = lmex10~at~7pt \scan_stop:
+ \tex_font:D \exp_not:c { OT1/lmr/m/n/5 }
+ = rm-lmr5 \scan_stop:
+ \tex_font:D \exp_not:c { OML/lmm/m/it/5 }
+ = lmmi5 \scan_stop:
+ \tex_font:D \exp_not:c { OMS/lmsy/m/n/5 }
+ = lmsy5 \scan_stop:
+ \tex_font:D \exp_not:c { OMX/lmex/m/n/5 }
+ = lmex10~at~5pt \scan_stop:
+ \tex_textfont:D 0 = \exp_not:c { OT1/lmr/m/n/10 }
+ \tex_textfont:D 1 = \exp_not:c { OML/lmm/m/it/10 }
+ \tex_textfont:D 2 = \exp_not:c { OMS/lmsy/m/n/10 }
+ \tex_textfont:D 3 = \exp_not:c { OMX/lmex/m/n/10 }
+ \tex_scriptfont:D 0 = \exp_not:c { OT1/lmr/m/n/7 }
+ \tex_scriptfont:D 1 = \exp_not:c { OML/lmm/m/it/7 }
+ \tex_scriptfont:D 2 = \exp_not:c { OMS/lmsy/m/n/7 }
+ \tex_scriptfont:D 3 = \exp_not:c { OMX/lmex/m/n/7 }
+ \tex_scriptscriptfont:D 0 = \exp_not:c { OT1/lmr/m/n/5 }
+ \tex_scriptscriptfont:D 1 = \exp_not:c { OML/lmm/m/it/5 }
+ \tex_scriptscriptfont:D 2 = \exp_not:c { OMS/lmsy/m/n/5 }
+ \tex_scriptscriptfont:D 3 = \exp_not:c { OMX/lmex/m/n/5 }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% Produce PDF output if possible (easier testing) and set some kind of
+% horizontal width: the one here is the \LaTeXe{} default. A parfill is
+% also useful so we get some kind of sensible paragraphs.
+% \begin{macrocode}
+\dim_set:Nn \tex_hsize:D { 345pt }
+\skip_set:Nn \tex_parfillskip:D { 0pt plus 1fil }
+\cs_if_exist:NT \tex_pdfoutput:D
+ { \int_set:Nn \tex_pdfoutput:D { 1 } }
+% \end{macrocode}
+%
+% \begin{macro}{\stop}
+% A way out of the run without needing to switch to the code environment.
+% \begin{macrocode}
+\cs_set_eq:NN \stop \tex_end:D
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Final tasks}
+%
+% \begin{macro}{\par}
+% \TeX{} has a nasty habit of inserting a command with the name \cs{par}
+% so we had better make sure that \cs{par} has a definition.
+% \begin{macrocode}
+\cs_set_eq:NN \par \tex_par:D
+% \end{macrocode}
+% \end{macro}
+%
+% The very last job is to dump the format, taking care to first leave
+% the code environment and set the appropriate flag.
+% \begin{macrocode}
+\use:n
+ {
+ \bool_set_false:N \l__kernel_expl_bool
+ \char_set_catcode_space:n { 9 } % tab
+ \char_set_catcode_space:n { 32 } % space
+ \char_set_catcode_active:n { 34 } % double quote
+ \char_set_catcode_active:n { 36 } % dollar
+ \char_set_catcode_active:n { 38 } % ampersand
+ \char_set_catcode_other:n { 58 } % colon
+ \char_set_catcode_active:n { 94 } % circumflex
+ \char_set_catcode_active:n { 95 } % underscore
+ \char_set_catcode_other:n { 124 } % pipe
+ \char_set_catcode_active:n { 126 } % tilde
+ \tex_endlinechar:D = 13 \scan_stop:
+ \tex_newlinechar:D = 10 \scan_stop:
+ \tex_dump:D
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+%\PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3flag.dtx b/macros/latex/contrib/l3kernel/l3flag.dtx
new file mode 100644
index 0000000000..7b829b4418
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3flag.dtx
@@ -0,0 +1,335 @@
+% \iffalse meta-comment
+%
+%% File: l3flag.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3flag} package: Expandable flags^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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 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. They are referenced by a \meta{flag
+% name} such as \texttt{str_missing}. The \meta{flag name} is used as
+% part of \cs{use:c} constructions hence is expanded at point of use.
+% It must expand to character tokens only, with no spaces.
+%
+% 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.
+% Flags should not be used unless unavoidable.
+%
+% \section{Setting up flags}
+%
+% \begin{function}{\flag_new:n}
+% \begin{syntax}
+% \cs{flag_new:n} \Arg{flag name}
+% \end{syntax}
+% Creates a new flag with a name given by \meta{flag name}, or raises
+% an error if the name is already taken. The \meta{flag name} may not
+% contain spaces. The declaration is global, but flags are always
+% local variables. The \meta{flag} initially has zero height.
+% \end{function}
+%
+% \begin{function}{\flag_clear:n}
+% \begin{syntax}
+% \cs{flag_clear:n} \Arg{flag name}
+% \end{syntax}
+% The \meta{flag}'s height is set to zero. The assignment is local.
+% \end{function}
+%
+% \begin{function}{\flag_clear_new:n}
+% \begin{syntax}
+% \cs{flag_clear_new:n} \Arg{flag name}
+% \end{syntax}
+% Ensures that the \meta{flag} 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}{\flag_show:n}
+% \begin{syntax}
+% \cs{flag_show:n} \Arg{flag name}
+% \end{syntax}
+% Displays the \meta{flag}'s height in the terminal.
+% \end{function}
+%
+% \begin{function}{\flag_log:n}
+% \begin{syntax}
+% \cs{flag_log:n} \Arg{flag name}
+% \end{syntax}
+% Writes the \meta{flag}'s height to the log file.
+% \end{function}
+%
+% \section{Expandable flag commands}
+%
+% \begin{function}[EXP,pTF]{\flag_if_exist:n}
+% \begin{syntax}
+% \cs{flag_if_exist:n} \Arg{flag name}
+% \end{syntax}
+% This function returns \texttt{true} if the \meta{flag name}
+% references a flag that has been defined previously, and
+% \texttt{false} otherwise.
+% \end{function}
+%
+% \begin{function}[EXP,pTF]{\flag_if_raised:n}
+% \begin{syntax}
+% \cs{flag_if_raised:n} \Arg{flag name}
+% \end{syntax}
+% This function returns \texttt{true} if the \meta{flag} has non-zero
+% height, and \texttt{false} if the \meta{flag} has zero height.
+% \end{function}
+%
+% \begin{function}[EXP]{\flag_height:n}
+% \begin{syntax}
+% \cs{flag_height:n} \Arg{flag name}
+% \end{syntax}
+% Expands to the height of the \meta{flag} as an integer denotation.
+% \end{function}
+%
+% \begin{function}[EXP]{\flag_raise:n}
+% \begin{syntax}
+% \cs{flag_raise:n} \Arg{flag name}
+% \end{syntax}
+% The \meta{flag}'s height is increased by $1$ locally.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3flag} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=flag>
+% \end{macrocode}
+%
+% \TestFiles{m3flag001}
+%
+% \subsection{Non-expandable flag commands}
+%
+% The height $h$ of a flag (initially zero) is stored by setting control
+% sequences of the form \cs[no-index]{flag \meta{name} \meta{integer}}
+% to \tn{relax} for $0\leq\meta{integer}<h$. When a flag is raised, a
+% \enquote{trap} function \cs[no-index]{flag \meta{name}} is called.
+% The existence of this function is also used to test for the existence
+% of a flag.
+%
+% \begin{macro}{\flag_new:n}
+% For each flag, we define a \enquote{trap} function, which by default
+% simply increases the flag by $1$ by letting the appropriate control
+% sequence to \tn{relax}. This can be done expandably!
+% \begin{macrocode}
+\cs_new_protected:Npn \flag_new:n #1
+ {
+ \cs_new:cpn { flag~#1 } ##1 ;
+ { \exp_after:wN \use_none:n \cs:w flag~#1~##1 \cs_end: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\flag_clear:n}
+% \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.
+% When the option \texttt{check-declarations} is used, check for the
+% function defined by \cs{flag_new:n}.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn
+ { \exp_args:Nc \__kernel_chk_var_exist:N { flag~#1 } } { }
+\cs_new_protected:Npn \flag_clear:n #1 { \@@_clear:wn 0 ; {#1} }
+\cs_new_protected:Npn \@@_clear:wn #1 ; #2
+ {
+ \if_cs_exist:w flag~#2~#1 \cs_end:
+ \cs_set_eq:cN { flag~#2~#1 } \tex_undefined:D
+ \exp_after:wN \@@_clear:wn
+ \int_value:w \int_eval:w 1 + #1
+ \else:
+ \use_i:nnn
+ \fi:
+ ; {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\flag_clear_new:n}
+% As for other datatypes, clear the \meta{flag} 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} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\flag_show:n, \flag_log:n, \@@_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_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 { flag~#2 }
+ {
+ \exp_args:Nx #1
+ { \tl_to_str:n { flag~#2~height } = \flag_height:n {#2} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expandable flag commands}
+%
+% \begin{macro}[EXP]{\@@_chk_exist:n}
+% Analogue of \cs{__kernel_chk_var_exist:N} for flags, and with an
+% expandable error. We need to add checks by hand because flags are
+% not implemented in terms of other variables. Not all functions need
+% to be patched since some are defined in terms of others.
+% \begin{macrocode}
+%<*package>
+\__kernel_if_debug:TF
+ {
+ \cs_new:Npn \@@_chk_exist:n #1
+ {
+ \flag_if_exist:nF {#1}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { bad-variable } { flag~#1~ }
+ }
+ }
+ }
+ { }
+%</package>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\flag_if_exist:n}
+% A flag exist if the corresponding trap \cs[no-index]{flag \meta{flag
+% name}:n} is defined.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \flag_if_exist:n #1 { p , T , F , TF }
+ {
+ \cs_if_exist:cTF { flag~#1 }
+ { \prg_return_true: } { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\flag_if_raised:n}
+% Test if the flag has a non-zero height, by checking the |0| control sequence.
+% \begin{macrocode}
+\__kernel_patch_conditional:nNNpnn { \@@_chk_exist:n {#1} }
+\prg_new_conditional:Npnn \flag_if_raised:n #1 { p , T , F , TF }
+ {
+ \if_cs_exist:w flag~#1~0 \cs_end:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\flag_height:n}
+% \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}
+\__kernel_patch:nnNNpn { \@@_chk_exist:n {#1} } { }
+\cs_new:Npn \flag_height:n #1 { \@@_height_loop:wn 0; {#1} }
+\cs_new:Npn \@@_height_loop:wn #1 ; #2
+ {
+ \if_cs_exist:w flag~#2~#1 \cs_end:
+ \exp_after:wN \@@_height_loop:wn \int_value:w \int_eval:w 1 +
+ \else:
+ \exp_after:wN \@@_height_end:wn
+ \fi:
+ #1 ; {#2}
+ }
+\cs_new:Npn \@@_height_end:wn #1 ; #2 {#1}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\flag_raise:n}
+% Simply apply the trap to the height, after expanding the latter.
+% \begin{macrocode}
+\cs_new:Npn \flag_raise:n #1
+ {
+ \cs:w flag~#1 \exp_after:wN \cs_end:
+ \int_value:w \flag_height:n {#1} ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3format.ins b/macros/latex/contrib/l3kernel/l3format.ins
new file mode 100644
index 0000000000..35279ca592
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3format.ins
@@ -0,0 +1,158 @@
+\iffalse meta-comment
+
+File l3format.ins
+
+Copyright (C) 2011,2012,2014-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 l3docstrip.dtx
+\askforoverwritefalse
+
+\preamble
+
+Copyright (C) 1990-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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{l3format.ltx}
+ {
+ \from{l3bootstrap.dtx} {initex}
+ \from{l3names.dtx} {initex}
+ \from{l3basics.dtx} {initex}
+ \from{l3expan.dtx} {initex}
+ \from{l3tl.dtx} {initex}
+ \from{l3str.dtx} {initex}
+ \from{l3quark.dtx} {initex}
+ \from{l3seq.dtx} {initex}
+ % ======== FORMAT ONLY =========
+ \from{l3alloc.dtx} {initex}
+ % ==============================
+ \from{l3int.dtx} {initex}
+ \from{l3flag.dtx} {initex}
+ \from{l3prg.dtx} {initex}
+ \from{l3sys.dtx} {initex}
+ \from{l3clist.dtx} {initex}
+ \from{l3token.dtx} {initex}
+ \from{l3prop.dtx} {initex}
+ \from{l3msg.dtx} {initex}
+ \from{l3file.dtx} {initex}
+ \from{l3skip.dtx} {initex}
+ \from{l3keys.dtx} {initex}
+ \from{l3intarray.dtx} {initex}
+ \from{l3fp.dtx} {initex}
+ \from{l3fp-aux.dtx} {initex}
+ \from{l3fp-traps.dtx} {initex}
+ \from{l3fp-round.dtx} {initex}
+ \from{l3fp-parse.dtx} {initex}
+ \from{l3fp-assign.dtx} {initex}
+ \from{l3fp-logic.dtx} {initex}
+ \from{l3fp-basics.dtx} {initex}
+ \from{l3fp-extended.dtx}{initex}
+ \from{l3fp-expo.dtx} {initex}
+ \from{l3fp-trig.dtx} {initex}
+ \from{l3fp-convert.dtx} {initex}
+ \from{l3fp-random.dtx} {initex}
+ \from{l3fparray.dtx} {initex}
+ \from{l3sort.dtx} {initex}
+ \from{l3str-convert.dtx}{initex}
+ \from{l3tl-analysis.dtx}{initex}
+ \from{l3regex.dtx} {initex}
+ \from{l3box.dtx} {initex}
+ \from{l3color-base.dtx} {initex}
+ \from{l3coffins.dtx} {initex}
+ \from{l3luatex.dtx} {initex,tex}
+ \from{l3unicode.dtx} {initex}
+ \from{l3candidates.dtx} {initex}
+ \from{l3deprecation.dtx}{initex}
+ % ======== FORMAT ONLY =========
+ \from{l3final.dtx} {initex}
+ % ==============================
+ }
+ }
+
+% 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{l3docstrip.tex}{\from{l3docstrip.dtx}{program}}}
+
+% Lua code (currently identical to the package-mode version)
+
+\def\MetaPrefix{--}
+\preamble
+
+Copyright (C) 1990-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of
+the LaTeX Project Public License (LPPL), either version 1.3c of
+this license or (at your option) any later version. The latest
+version of this license is in the file:
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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}{lua,package}}}
+\generate{\file{l3format.lua}{\from{l3luatex.dtx}{lua,initex}}}
+
+\endbatchfile
diff --git a/macros/latex/contrib/l3kernel/l3fp-assign.dtx b/macros/latex/contrib/l3kernel/l3fp-assign.dtx
new file mode 100644
index 0000000000..0245f807f7
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-assign.dtx
@@ -0,0 +1,229 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-assign.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-assign} package\\
+% Floating point expressions}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-assign} implementation}
+%
+% \begin{macrocode}
+%<*initex|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
+ { \tl_set:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_gset:Nn #1#2
+ { \tl_gset:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } }
+\cs_new_protected:Npn \fp_const:Nn #1#2
+ { \tl_const:Nx #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_defined:NT #2
+ { \exp_args:Nx #1 { \token_to_str:N #2 = \fp_to_tl:N #2 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fp_show:n, \fp_log:n}
+% Use general tools.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_show:n
+ { \msg_show_eval:Nn \fp_to_tl:n }
+\cs_new_protected:Npn \fp_log:n
+ { \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-aux.dtx b/macros/latex/contrib/l3kernel/l3fp-aux.dtx
new file mode 100644
index 0000000000..616cce45f3
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-aux.dtx
@@ -0,0 +1,1291 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-aux.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3fp-aux} package\\ Support for floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-aux} implementation}
+%
+% \begin{macrocode}
+%<*initex|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{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
+ { \__kernel_msg_error:nnx { kernel } { misused-fp } { \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{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{macro}{\s_@@_mark, \s_@@_stop}
+% Aliases of \cs{tex_relax:D}, used to terminate expressions.
+% \begin{macrocode}
+\scan_new:N \s_@@_mark
+\scan_new:N \s_@@_stop
+% \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:Nx \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 }
+ \q_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{q_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 \q_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 }
+ \q_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 }
+ \q_stop
+ }
+\cs_new:Npx \@@_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 \exp_not:N \q_mark
+ \tl_to_str:n { s_@@ _? } \exp_not:N \q_mark \exp_not:N \q_stop
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_type_from_scan:w #1
+ { \tl_to_str:n { s_@@ } } #2 \q_mark #3 \q_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
+ }
+ \q_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_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 }
+ \q_stop
+ {#1} #2
+ }
+\cs_new_eq:NN \@@_exp_after_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_@@_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_@@_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 > 0 \exp_stop_f:
+ 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. As
+% the nature of the arguments is restricted and as speed is of the essence,
+% this version does not seek to deal with |#| tokens.
+% No \pkg{l3sys} or \pkg{l3luatex} just yet so we have to define in terms of
+% primitives.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_str_if_eq:nn #1#2
+ {
+ \tex_directlua:D
+ {
+ l3kernel.strcmp
+ (
+ " \tex_luaescapestring:D {#1}",
+ " \tex_luaescapestring:D {#2}"
+ )
+ }
+ }
+ }
+ { \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}
+\__kernel_msg_new:nnnn { kernel } { misused-fp }
+ { 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.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-basics.dtx b/macros/latex/contrib/l3kernel/l3fp-basics.dtx
new file mode 100644
index 0000000000..4c0e12ffcf
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-basics.dtx
@@ -0,0 +1,2178 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-basics.dtx
+%
+% Copyright (C) 2011-2014,2016-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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{The \textsf{l3fp-basics} package\\
+% Floating point arithmetic}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-basics} Implementation}
+%
+% \begin{macrocode}
+%<*initex|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:cpx { @@_-_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 -20 + 2*#2#3 - #1*#6#7 +
+ \exp_after:wN \@@_div_significand_v:NN
+ \int_value:w \@@_int_eval:w 199980 + 2*#4 - #1*#8 +
+ \exp_after:wN \@@_div_significand_v:NN
+ \int_value:w \@@_int_eval:w 200000 + 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 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 > 1 \exp_stop_f:
+ \exp_after:wN \@@_sqrt_auxiv_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2 %)
+ \else:
+ \if_int_compare:w #1#2 > 1 \exp_stop_f:
+ \exp_after:wN \@@_sqrt_auxv_o:NNNNNw
+ \int_value:w \@@_int_eval:w (#1#2#3 %)
+ \else:
+ \if_int_compare:w #1#2#3 > 1 \exp_stop_f:
+ \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 = 0 \exp_stop_f:
+ \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 > 0 \exp_stop_f:
+ \if_int_compare:w #1#2 = 1 \exp_stop_f:
+ \if_int_compare:w #3#4 = 0 \exp_stop_f:
+ \if_int_compare:w #5#6 = 0 \exp_stop_f:
+ \if_int_compare:w #7#8 = 0 \exp_stop_f:
+ \@@_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
+ {
+ \if_meaning:w 2 #1
+ \exp_after:wN \@@_tuple_set_sign_aux_o:Nnw
+ \fi:
+ \@@_invalid_operation_o:nw { abs }
+ }
+\cs_new:Npn \@@_tuple_set_sign_aux_o:Nnw #1#2#3 @
+ { \@@_tuple_map_o:nw \@@_tuple_set_sign_aux_o:w #3 }
+\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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-convert.dtx b/macros/latex/contrib/l3kernel/l3fp-convert.dtx
new file mode 100644
index 0000000000..cd545aec51
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-convert.dtx
@@ -0,0 +1,621 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-convert.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3fp-convert} package\\ Floating point conversion^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\texttt{l3fp-convert} implementation}
+%
+% \begin{macrocode}
+%<*initex|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 { fp-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 { fp-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 > 0 \exp_stop_f:
+ #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 { fp-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}
+\__kernel_patch_args:nNNpn { { (#1) } }
+\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}[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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-expo.dtx b/macros/latex/contrib/l3kernel/l3fp-expo.dtx
new file mode 100644
index 0000000000..72c4020d6c
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-expo.dtx
@@ -0,0 +1,1379 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-expo.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-expo} package\\
+% Floating point exponential-related functions}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-expo} implementation}
+%
+% \begin{macrocode}
+%<*initex|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 < 1 \exp_stop_f:
+ 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 = 1 \exp_stop_f:
+ \@@_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 > 0 \exp_stop_f:
+ \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 < 0 \exp_stop_f:
+ \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 = 1 \exp_stop_f:
+ \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_int_compare:w \@@_str_if_eq:nn { #2 #3 }
+ { 1 {1000} {0000} {0000} {0000} } = 0 \exp_stop_f:
+ \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 > 0 \exp_stop_f:
+ \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 > 0 \exp_stop_f: 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 > 0 \exp_stop_f:
+ \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 < 0 \exp_stop_f:
+ \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 = 1 \exp_stop_f:
+ \@@_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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-extended.dtx b/macros/latex/contrib/l3kernel/l3fp-extended.dtx
new file mode 100644
index 0000000000..d78a029038
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-extended.dtx
@@ -0,0 +1,1273 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-extended.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-extended} package\\
+% Manipulating numbers with extended precision, for internal use}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-extended} implementation}
+%
+% \begin{macrocode}
+%<*initex|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}
+% \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-logic.dtx b/macros/latex/contrib/l3kernel/l3fp-logic.dtx
new file mode 100644
index 0000000000..c0584cde42
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-logic.dtx
@@ -0,0 +1,737 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-logic.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-logic} package\\
+% Floating point conditionals}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-logic} Implementation}
+%
+% \begin{macrocode}
+%<*initex|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_q_stop:nw #3 \q_stop }
+ \s_@@ 1 \q_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_any:ww, \@@_compare_back: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_any:ww #1#2; #3
+ {
+ \@@_if_type_fp:NTwFw
+ #1 { \@@_if_type_fp:NTwFw #3 \use_i:nn \s_@@ \use_ii:nn \q_stop }
+ \s_@@ \use_ii:nn \q_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 \@@_compare_back: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 = 0 \exp_stop_f:
+ 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 ; = 0 \exp_stop_f:
+ \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: \q_stop
+ \@@_if_type_fp:NTwFw #3 { } \s_@@ \prg_break: \q_stop
+ \@@_if_type_fp:NTwFw #5 { } \s_@@ \prg_break: \q_stop
+ \use_i:nnnn { \@@_step_fp:wwwN #1#2; #3#4; #5#6; #7 }
+ \prg_break_point:
+ \use:n
+ {
+ \@@_error:nfff { fp-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
+ {
+ \__kernel_msg_expandable_error:nnn { kernel }
+ { zero-step } {#6}
+ }
+ {
+ \@@_error:nnfn { fp-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 { fp-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:Npx
+ { @@_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}
+\__kernel_msg_new:nnn { kernel } { fp-step-tuple }
+ { Tuple~argument~in~fp_step_...~{#1}{#2}{#3}. }
+\__kernel_msg_new:nnn { kernel } { fp-bad-step }
+ { Invalid~step~size~#2~in~step~function~#3. }
+\__kernel_msg_new:nnn { kernel } { fp-tiny-step }
+ { Tiny~step~size~(#1+#2=#1)~in~step~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_q_stop:nw #3 \q_stop }
+ \s_@@ 1 \q_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_@@_stop
+ \exp_after:wN @
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_colon_int
+ \@@_parse_expand:w
+ \else:
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { 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_@@_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_@@_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_@@_stop
+ #4 #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-parse.dtx b/macros/latex/contrib/l3kernel/l3fp-parse.dtx
new file mode 100644
index 0000000000..b3d7b32bc8
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-parse.dtx
@@ -0,0 +1,2908 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-parse.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-parse} package\\
+% Floating point expression parsing}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-parse} implementation}
+%
+% \begin{macrocode}
+%<*initex|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{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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_@@_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_@@_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_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_@@_mark} is a premature end, we call
+% \cs{@@_exp_after_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_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
+ }
+ }
+ {
+ \__kernel_msg_expandable_error:nn { kernel } { 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
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { bad-variable }
+ {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w #1
+ }
+%<*package>
+\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 }
+ {
+ \__kernel_msg_expandable_error:nnn { kernel }
+ { fp-robust-cmd }
+ }
+ }
+ {
+ \__kernel_msg_expandable_error:nnn { kernel }
+ { bad-variable } {##2}
+ }
+ }
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @unexpandable@protect }
+%</package>
+% \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:Npx \@@_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
+ \exp_not:N \q_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_parse_one_register_auxii:wwwNw #1 . #2 }
+ { \tl_to_str:n { pt } #3 ; #4#5 \q_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}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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_fold_case:n {#2} :N }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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 }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing-number } {#1}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \@@_parse_infix:NN #3 #1
+ }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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:N}
+% 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|, leave an exponent of~$0$. If
+% there is an~|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 \exp_not:N #1
+ \exp_after:wN \@@_parse_exponent_aux:N
+ \exp:w
+ \else:
+ 0 \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_exponent_aux:N #1
+ {
+ \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #1
+ 0 \else: `#1 \fi: > `9 \exp_stop_f:
+ 0 \exp_after:wN ; \exp_after:wN e
+ \else:
+ \exp_after:wN \@@_parse_exponent_sign:N
+ \fi:
+ #1
+ }
+% \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_int_compare:w
+ \@@_str_if_eq:nn { \s_@@ } { \exp_not:N #1 }
+ = 0 \exp_stop_f:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-after-e } { floating~point~ }
+ \prg_return_true:
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ \prg_return_false:
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_str_if_eq:nn { \int_value:w #1 } { \tex_the:D #1 }
+ = 0 \exp_stop_f:
+ \int_value:w #1
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-after-e } { dimension~#1 }
+ \fi:
+ \prg_return_false:
+ \fi:
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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 @ ; . \q_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 \q_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 { fp-#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:Npx \@@_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_@@_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 \__kernel_msg_expandable_error:nnn
+ { kernel } { 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:
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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_@@_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_@@_mark \@@_parse_infix_end:N
+ \s_@@_stop
+ \exp_end:
+ }
+\cs_new:Npn \@@_parse_after:ww
+ #1@ \@@_parse_infix_end:N \s_@@_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_@@_mark \@@_parse_infix_end:N
+ \s_@@_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_int_compare:w
+ \@@_str_if_eq:nn { \s_@@_mark } { \exp_not:N #2 }
+ = 0 \exp_stop_f:
+ \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
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { 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_int_compare:w
+ \@@_str_if_eq:nn { \s_@@_mark } { \exp_not:N #2 }
+ = 0 \exp_stop_f:
+ \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_@@_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:
+ \__kernel_msg_expandable_error:nnn { kernel } { 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
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { 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:
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { 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 { fp-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 \q_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 { fp-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
+ }
+ \q_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}
+\__kernel_msg_new:nnn { kernel } { fp-deprecated }
+ { '#1'~deprecated;~use~'#2' }
+\__kernel_msg_new:nnn { kernel } { unknown-fp-word }
+ { Unknown~fp~word~#1. }
+\__kernel_msg_new:nnn { kernel } { fp-missing }
+ { Missing~#1~inserted #2. }
+\__kernel_msg_new:nnn { kernel } { fp-extra }
+ { Extra~#1~ignored. }
+\__kernel_msg_new:nnn { kernel } { fp-early-end }
+ { Premature~end~in~fp~expression. }
+\__kernel_msg_new:nnn { kernel } { fp-after-e }
+ { Cannot~use~#1 after~'e'. }
+\__kernel_msg_new:nnn { kernel } { fp-missing-number }
+ { Missing~number~before~'#1'. }
+\__kernel_msg_new:nnn { kernel } { fp-unknown-symbol }
+ { Unknown~symbol~#1~ignored. }
+\__kernel_msg_new:nnn { kernel } { fp-extra-comma }
+ { Unexpected~comma~turned~to~nan~result. }
+\__kernel_msg_new:nnn { kernel } { fp-no-arg }
+ { #1~got~no~argument;~used~nan. }
+\__kernel_msg_new:nnn { kernel } { fp-multi-arg }
+ { #1~got~more~than~one~argument;~used~nan. }
+\__kernel_msg_new:nnn { kernel } { fp-num-args }
+ { #1~expects~between~#2~and~#3~arguments. }
+\__kernel_msg_new:nnn { kernel } { fp-bad-args }
+ { Arguments~in~#1#2~are~invalid. }
+\__kernel_msg_new:nnn { kernel } { fp-infty-pi }
+ { Math~command~#1 is~not~an~fp }
+%<*package>
+\cs_if_exist:cT { @unexpandable@protect }
+ {
+ \__kernel_msg_new:nnn { kernel } { fp-robust-cmd }
+ { Robust~command~#1 invalid~in~fp~expression! }
+ }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-random.dtx b/macros/latex/contrib/l3kernel/l3fp-random.dtx
new file mode 100644
index 0000000000..9c74c70774
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-random.dtx
@@ -0,0 +1,664 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-random.dtx
+%
+% Copyright (C) 2016-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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{The \textsf{l3fp-random} package\\
+% Floating point random numbers}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-random} Implementation}
+%
+% \begin{macrocode}
+%<*initex|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}
+%
+% Most engines provide random numbers, but not all. We write the test
+% twice simply in order to write the \texttt{false} branch first.
+% \begin{macrocode}
+\sys_if_rand_exist:F
+ {
+ \__kernel_msg_new:nnn { kernel } { fp-no-random }
+ { Random~numbers~unavailable~for~#1 }
+ \cs_new:Npn \@@_rand_o:Nw ? #1 @
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-no-random }
+ { fp~rand }
+ \exp_after:wN \c_nan_fp
+ }
+ \cs_new_eq:NN \@@_randint_o:Nw \@@_rand_o:Nw
+ \cs_new:Npn \int_rand:nn #1#2
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-no-random }
+ { \int_rand:nn {#1} {#2} }
+ \int_eval:n {#1}
+ }
+ \cs_new:Npn \int_rand:n #1
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-no-random }
+ { \int_rand:n {#1} }
+ 1
+ }
+ }
+\sys_if_rand_exist:T
+ {
+% \end{macrocode}
+%
+% 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
+ }
+ {
+ \__kernel_msg_expandable_error:nnnnn
+ { kernel } { 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
+ 1 \exp_stop_f:
+ \fi:
+ \fi:
+ }
+ { 1 \exp_stop_f: }
+ }
+ \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; 1 \exp_stop_f: \fi:
+ 0 \exp_stop_f:
+ \@@_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:
+ \__kernel_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 < 1 \exp_stop_f:
+ \__kernel_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}
+%
+% End the initial conditional that ensures these commands are only
+% defined in engines that support random numbers.
+% \begin{macrocode}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-round.dtx b/macros/latex/contrib/l3kernel/l3fp-round.dtx
new file mode 100644
index 0000000000..9bedd90aab
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-round.dtx
@@ -0,0 +1,592 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-round.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3fp-round} package\\ Rounding floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-round} implementation}
+%
+% \begin{macrocode}
+%<*initex|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 > 0 \exp_stop_f:
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ 0 \exp_stop_f:
+ }
+\cs_new:Npn \@@_round_to_zero:NNN #1 #2 #3 { 0 \exp_stop_f: }
+\cs_new:Npn \@@_round_to_pinf:NNN #1 #2 #3
+ {
+ \if_meaning:w 0 #1
+ \if_int_compare:w #3 > 0 \exp_stop_f:
+ \@@_round_return_one:
+ \fi:
+ \fi:
+ 0 \exp_stop_f:
+ }
+\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:
+ 0 \exp_stop_f:
+ }
+\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:
+ 0 \exp_stop_f:
+ }
+\cs_new:Npn \@@_round_to_nearest_zero:NNN #1 #2 #3
+ {
+ \if_int_compare:w #3 > \c_@@_five_int
+ \@@_round_return_one:
+ \fi:
+ 0 \exp_stop_f:
+ }
+\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:
+ 0 \exp_stop_f:
+ }
+\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 > 0 \exp_stop_f:
+ 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{intexpr} |;|
+% \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 > 0 \exp_stop_f:
+ \@@_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 > 0 \exp_stop_f:
+ \@@_round_return_one:
+ \fi:
+ 0 \exp_stop_f:
+ }
+\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:
+ 0 \exp_stop_f:
+ }
+\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 { fp-num-args } { round () } { 1 } { 3 } }
+ {
+ \@@_error:nffn { fp-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 { fp-num-args } { round () } { 1 } { 3 }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+ {
+ \@@_error:nffn { fp-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 > 0 \exp_stop_f:
+ 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 > 0 \exp_stop_f:
+ 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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-traps.dtx b/macros/latex/contrib/l3kernel/l3fp-traps.dtx
new file mode 100644
index 0000000000..aad063f831
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-traps.dtx
@@ -0,0 +1,400 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-traps.dtx
+%
+% Copyright (C) 2011-2014,2016-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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{The \textsf{l3fp-traps} package\\
+% Trapping floating-point exceptions}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-traps} Implementation}
+%
+% \begin{macrocode}
+%<*initex|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}
+% {
+% flag fp_invalid_operation,
+% flag fp_division_by_zero,
+% flag fp_overflow,
+% flag fp_underflow
+% }
+% Flags to denote exceptions.
+% \begin{macrocode}
+\flag_new:n { fp_invalid_operation }
+\flag_new:n { fp_division_by_zero }
+\flag_new:n { fp_overflow }
+\flag_new:n { fp_underflow }
+% \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}
+ {
+ \__kernel_msg_error:nnxx { kernel }
+ { unknown-fpu-trap-type } {#1} {#2}
+ }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { 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 { fp-invalid } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_o:Nww ##1##2; ##3; }
+ {
+ #1
+ \@@_error:nffn { fp-invalid-ii }
+ { \fp_to_tl:n { ##2; } } { \fp_to_tl:n { ##3; } } {##1}
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ \exp_after:wN \c_nan_fp
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_tl_o:ff ##1##2 }
+ {
+ #1
+ \@@_error:nffn { fp-invalid } {##1} {##2} { }
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ \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 { fp-zero-div } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_raise_if_clear:n { fp_division_by_zero }
+ \exp_after:wN ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_division_by_zero_o:NNww ##1##2##3; ##4; }
+ {
+ #1
+ \@@_error:nffn { fp-zero-div-ii }
+ { \fp_to_tl:n { ##3; } } { \fp_to_tl:n { ##4; } } {##2}
+ \flag_raise_if_clear:n { fp_division_by_zero }
+ \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
+ { fp-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_raise_if_clear:n { fp_#2 }
+ #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
+ { \__kernel_msg_expandable_error:nnnnn { kernel } }
+\cs_generate_variant:Nn \@@_error:nnnn { nnf, nff , nfff }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% Some messages.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { 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
+ }
+ }
+\__kernel_msg_new:nnnn { kernel } { 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
+ }
+ }
+\__kernel_msg_new:nnn { kernel } { fp-flow }
+ { An ~ #3 ~ occurred. }
+\__kernel_msg_new:nnn { kernel } { fp-flow-to }
+ { #1 ~ #3 ed ~ to ~ #2 . }
+\__kernel_msg_new:nnn { kernel } { fp-zero-div }
+ { Division~by~zero~in~ #1 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-zero-div-ii }
+ { Division~by~zero~in~ (#1) #3 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-invalid }
+ { Invalid~operation~ #1 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-invalid-ii }
+ { Invalid~operation~ (#1) #3 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-unknown-type }
+ { Unknown~type~for~'#1' }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp-trig.dtx b/macros/latex/contrib/l3kernel/l3fp-trig.dtx
new file mode 100644
index 0000000000..085641bafd
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-trig.dtx
@@ -0,0 +1,1698 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-trig.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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{The \textsf{l3fp-trig} package\\
+% Floating point trigonometric functions}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-trig} Implementation}
+%
+% \begin{macrocode}
+%<*initex|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:cpx { @@_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:cpx { @@_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:Npx \@@_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; > 0 \exp_stop_f:
+ 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 = -1 \exp_stop_f:
+ \@@_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 < 1 \exp_stop_f:
+ \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3fp.dtx b/macros/latex/contrib/l3kernel/l3fp.dtx
new file mode 100644
index 0000000000..a74b27f900
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp.dtx
@@ -0,0 +1,1488 @@
+% \iffalse meta-comment
+%
+%% File: l3fp.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3fp} package: Floating points^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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. Floating point
+% expressions 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$.
+% \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 Rouning 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{xparse, 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{floating point expression}
+% \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{floating point expression}.
+% \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{floating point expression}
+% \end{syntax}
+% Sets \meta{fp~var} equal to the result of computing the
+% \meta{floating point expression}.
+% \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{floating point expression}
+% \end{syntax}
+% Adds the result of computing the \meta{floating point expression} 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{floating point expression}
+% \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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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 = 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression}, 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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{fpexpr_1} \meta{relation} \Arg{fpexpr_2}
+% \cs{fp_compare:nNnTF} \Arg{fpexpr_1} \meta{relation} \Arg{fpexpr_2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the \meta{fpexpr_1} and the \meta{fpexpr_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{fpexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{fpexpr_N} \meta{relation_N} \\
+% ~~~~\meta{fpexpr_{N+1}} \\
+% ~~\} \\
+% \cs{fp_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{fpexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{fpexpr_N} \meta{relation_N} \\
+% ~~~~\meta{fpexpr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Evaluates the \meta{floating point expressions} as described for
+% \cs{fp_eval:n} and compares consecutive result using the
+% corresponding \meta{relation}, namely it compares \meta{intexpr_1}
+% and \meta{intexpr_2} using the \meta{relation_1}, then
+% \meta{intexpr_2} and \meta{intexpr_3} using the \meta{relation_2},
+% until finally comparing \meta{intexpr_N} and \meta{intexpr_{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{floating point expressions} 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}
+%
+% \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{fpexpr_1} \meta{relation} \Arg{fpexpr_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{fpexpr_1} \meta{relation} \Arg{fpexpr_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{fpexpr_1} \meta{relation} \Arg{fpexpr_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{fpexpr_1} \meta{relation} \Arg{fpexpr_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{fpexpr_1} \meta{relation} \meta{fpexpr_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{fpexpr_1} \meta{relation} \meta{fpexpr_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{fpexpr_1} \meta{relation} \meta{fpexpr_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{fpexpr_1} \meta{relation} \meta{fpexpr_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{Some useful constants, and scratch variables}
+%
+% \begin{variable}[added = 2012-05-08]{\c_zero_fp, \c_minus_zero_fp}
+% Zero, with either sign.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08]{\c_one_fp}
+% One as an \texttt{fp}: useful for comparisons in some places.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-05-08]{\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}[updated = 2012-05-08]{\c_e_fp}
+% The value of the base of the natural logarithm, $\mathrm{e} = \exp(1)$.
+% \end{variable}
+%
+% \begin{variable}[updated = 2013-11-17]{\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]
+% {\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}
+%
+% \begin{variable}{\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}{\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}: \texttt{fp_overflow},
+% \texttt{fp_underflow}, \texttt{fp_invalid_operation} and
+% \texttt{fp_division_by_zero}. 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}
+% {
+% flag fp_overflow,
+% flag fp_underflow,
+% flag fp_invalid_operation,
+% flag fp_division_by_zero
+% }
+% Flags denoting the occurrence of various floating-point exceptions.
+% \end{variable}
+%
+% \section{Viewing floating points}
+%
+% \begin{function}[added = 2012-05-08, updated = 2015-08-07,
+% 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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} and displays the
+% result in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-22, updated = 2015-08-07]
+% {\fp_log:N, \fp_log:c, \fp_log:n}
+% \begin{syntax}
+% \cs{fp_log:N} \meta{fp~var}
+% \cs{fp_log:n} \Arg{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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|, 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{c_e_fp}.
+%
+% 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} & = (1in)/(1cm) = 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]{?:}
+% \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{fpexpr} |)| \}
+% \end{syntax}
+% Computes the absolute value of the \meta{fpexpr}. 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{fpexpr} |)| \}
+% \end{syntax}
+% Computes the exponential of the \meta{fpexpr}. \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{fpexpr} |)| \}
+% \end{syntax}
+% Computes the factorial of the \meta{fpexpr}. If the \meta{fpexpr}
+% 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{fpexpr} |)| \}
+% \end{syntax}
+% Computes the natural logarithm of the \meta{fpexpr}. 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}[tested = m3fp-logic002]{max, min}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |max(| \meta{fpexpr_1} |,| \meta{fpexpr_2} |,| \ldots{} |)| \}
+% \cs{fp_eval:n} \{ |min(| \meta{fpexpr_1} |,| \meta{fpexpr_2} |,| \ldots{} |)| \}
+% \end{syntax}
+% Evaluates each \meta{fpexpr} and computes the largest (smallest) of
+% those. If any of the \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |round| |(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \}
+% \cs{fp_eval:n} \{ |round| |(| \meta{fpexpr_1} , \meta{fpexpr_2} , \meta{fpexpr_3} |)| \}
+% \end{syntax}
+% Only |round| accepts a third argument.
+% Evaluates $\meta{fpexpr_1}=x$ and $\meta{fpexpr_2}=n$ and $\meta{fpexpr_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{fpexpr_2} is omitted,
+% $n=0$, \emph{i.e.}, \meta{fpexpr_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 that 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{fpexpr_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{fpexpr} |)| \}
+% \end{syntax}
+% Evaluates the \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |cos(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |tan(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |cot(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |csc(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |sec(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Computes the sine, cosine, tangent, cotangent, cosecant, or secant
+% of the \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |cosd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |tand(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |cotd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |cscd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |secd(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Computes the sine, cosine, tangent, cotangent, cosecant, or secant
+% of the \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acos(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acsc(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |asec(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Computes the arcsine, arccosine, arccosecant, or arcsecant of the
+% \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acosd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acscd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |asecd(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Computes the arcsine, arccosine, arccosecant, or arcsecant of the
+% \meta{fpexpr} and returns the result in degrees, in the range
+% $[-90,90]$ for \texttt{asin} and \texttt{acsc} and $[0,180]$ for
+% \texttt{acos} and \texttt{asec}. For a result in radians, use
+% \texttt{asin}, \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-trig002]
+% {atan, acot}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |atan(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |atan(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \}
+% \cs{fp_eval:n} \{ |acot(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acot(| \meta{fpexpr_1} , \meta{fpexpr_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{fpexpr}: 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{fpexpr_2},
+% \meta{fpexpr_1})$: this is the arctangent of
+% $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted by~$\pi$
+% depending on the signs of \meta{fpexpr_1} and \meta{fpexpr_2}. The
+% two-argument arccotangent computes the angle in polar coordinates of
+% the point $(\meta{fpexpr_1}, \meta{fpexpr_2})$, equal to the
+% arccotangent of $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted
+% by~$\pi$. Both two-argument functions take values in the wider
+% range $[-\pi,\pi]$. The ratio $\meta{fpexpr_1}/\meta{fpexpr_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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |atand(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \}
+% \cs{fp_eval:n} \{ |acotd(| \meta{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |acotd(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \}
+% \end{syntax}
+% Those functions yield an angle in degrees: \texttt{atand} and
+% \texttt{acotd} are their analogs in radians. The one-argument
+% versions compute the arctangent or arccotangent of the
+% \meta{fpexpr}: 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{fpexpr_2}, \meta{fpexpr_1})$: this is the
+% arctangent of $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted
+% by~$180$ depending on the signs of \meta{fpexpr_1} and
+% \meta{fpexpr_2}. The two-argument arccotangent computes the angle
+% in polar coordinates of the point $(\meta{fpexpr_1},
+% \meta{fpexpr_2})$, equal to the arccotangent of
+% $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted by~$180$. Both
+% two-argument functions take values in the wider range $[-180,180]$.
+% The ratio $\meta{fpexpr_1}/\meta{fpexpr_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{fpexpr} |)| \}
+% \end{syntax}
+% Computes the square root of the \meta{fpexpr}. The \enquote{invalid
+% operation} is raised when the \meta{fpexpr} 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{fpexpr} |)| \}
+% \cs{fp_eval:n} \{ |randint(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \}
+% \end{syntax}
+% Produces a pseudo-random integer between $1$~and \meta{fpexpr} or
+% between \meta{fpexpr_1} and \meta{fpexpr_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{c_inf_fp},
+% \cs{c_minus_inf_fp} and \cs{c_nan_fp}).
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]{pi}
+% The value of $\pi$ (see \cs{c_pi_fp}).
+% \end{variable}
+%
+% \begin{variable}[tested = m3fp-parse001]{deg}
+% The value of $1^{\circ}$ in radians (see \cs{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]{\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.52587890625e-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{floating point expression}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} 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 expression 1} \Arg{fp expression 2}
+% \end{syntax}
+% Evaluates the \meta{floating point expressions} 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}
+%
+% The package may break down 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{fpexpr} \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{__fp_basics_pack_weird_low:NNNNw}
+% and \cs{__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/macros/latex/contrib/l3kernel/l3fparray.dtx b/macros/latex/contrib/l3kernel/l3fparray.dtx
new file mode 100644
index 0000000000..5a80f4cfe0
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fparray.dtx
@@ -0,0 +1,333 @@
+% \iffalse meta-comment
+%
+%% File: l3fparray.dtx
+%
+% Copyright (C) 2018-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3fparray} package: fast global floating point arrays^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3fparray} 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
+%
+% Currently \emph{all} functions in this module are candidates. Their
+% documentation can be found in \pkg{l3candidates}.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fparray} implementation}
+%
+% \begin{macrocode}
+%<*initex|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, \@@_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_new_protected:Npn \@@_array_new:nNNNN #1#2#3#4#5
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \__kernel_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}
+%
+% \begin{macro}[EXP]{\fparray_count:N}
+% 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
+ }
+% \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, \@@_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_new_protected:Npn \@@_array_gset:NNNNww #1#2#3#4#5 ; #6 ;
+ {
+ \@@_array_bounds:NNnTF \__kernel_msg_error:nnxxx #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 { fp-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}
+%
+% \begin{macro}{\fparray_gzero:N}
+% \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
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \fparray_item:Nn,
+% \fparray_item_to_tl:Nn,
+% \@@_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_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_new:Npn \@@_array_item:NwN #1#2 ; #3
+ {
+ \@@_array_bounds:NNnTF \__kernel_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}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3int.dtx b/macros/latex/contrib/l3kernel/l3int.dtx
new file mode 100644
index 0000000000..7c0dd3caec
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3int.dtx
@@ -0,0 +1,2603 @@
+% \iffalse meta-comment
+%
+%% File: l3int.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Integers^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{\texttt{intexpr}}).
+%
+% \section{Integer expressions}
+%
+% \begin{function}[EXP]{\int_eval:n}
+% \begin{syntax}
+% \cs{int_eval:n} \Arg{integer expression}
+% \end{syntax}
+% Evaluates the \meta{integer expression} 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{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;
+% \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{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
+% 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{integer expression}
+% \end{syntax}
+% Evaluates the \meta{integer expression} 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| expands to \texttt{29}
+% since the digit~\texttt{9} is not part of the expression.
+% \end{function}
+%
+% \begin{function}[EXP, updated = 2012-09-26]{\int_abs:n}
+% \begin{syntax}
+% \cs{int_abs:n} \Arg{integer expression}
+% \end{syntax}
+% Evaluates the \meta{integer expression} 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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Evaluates the two \meta{integer expressions} 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{integer expression}. 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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Evaluates the two \meta{integer expressions} 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{intexpr_1} \Arg{intexpr_2}
+% \cs{int_min:nn} \Arg{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Evaluates the \meta{integer expressions} 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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Evaluates the two \meta{integer expressions} 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{intexpr_1} \Arg{intexpr_2} times
+% \meta{intexpr_2} from \meta{intexpr_1}. Thus, the result has the
+% same sign as \meta{intexpr_1} and its absolute value is strictly
+% less than that of \meta{intexpr_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{integer expression}
+% \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{integer expression}.
+% \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{integer expression}
+% \end{syntax}
+% Adds the result of the \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Sets \meta{integer} to the value of \meta{integer expression},
+% 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{integer expression}
+% \end{syntax}
+% Subtracts the result of the \meta{integer expression} 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{intexpr_1} \meta{relation} \Arg{intexpr_2} \\
+% \cs{int_compare:nNnTF}
+% ~~\Arg{intexpr_1} \meta{relation} \Arg{intexpr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the \meta{integer expressions}
+% 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{intexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{intexpr_N} \meta{relation_N} \\
+% ~~~~\meta{intexpr_{N+1}} \\
+% ~~\} \\
+% \cs{int_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{intexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{intexpr_N} \meta{relation_N} \\
+% ~~~~\meta{intexpr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{integer expressions} as described
+% for \cs{int_eval:n} and compares consecutive result using the
+% corresponding \meta{relation}, namely it compares \meta{intexpr_1}
+% and \meta{intexpr_2} using the \meta{relation_1}, then
+% \meta{intexpr_2} and \meta{intexpr_3} using the \meta{relation_2},
+% until finally comparing \meta{intexpr_N} and \meta{intexpr_{N+1}}
+% using the \meta{relation_N}. The test yields \texttt{true} if all
+% comparisons are \texttt{true}. Each \meta{integer expression} 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 integer expression} \\
+% ~~|{| \\
+% ~~~~\Arg{intexpr case_1} \Arg{code case_1} \\
+% ~~~~\Arg{intexpr case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{intexpr case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{test integer expression} and
+% compares this in turn to each of the
+% \meta{integer expression 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{integer expression}
+% \cs{int_if_odd:nTF} \Arg{integer expression}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates the \meta{integer expression}
+% as described for \cs{int_eval:n}. It then evaluates if this
+% is odd or even, as appropriate.
+% \end{function}
+%
+% \section{Integer expression loops}
+%
+% \begin{function}[rEXP]{\int_do_until:nNnn}
+% \begin{syntax}
+% \cs{int_do_until:nNnn} \Arg{intexpr_1} \meta{relation} \Arg{intexpr_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{integer expressions} 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{intexpr_1} \meta{relation} \Arg{intexpr_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{integer expressions} 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{intexpr_1} \meta{relation} \Arg{intexpr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{integer expressions}
+% 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{intexpr_1} \meta{relation} \Arg{intexpr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{integer expressions}
+% 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}
+% \begin{syntax}
+% \cs{int_to_arabic:n} \Arg{integer expression}
+% \end{syntax}
+% Places the value of the \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Evaluates the \meta{integer expression} 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{integer expression} \Arg{total symbols}
+% ~~\Arg{value to symbol mapping}
+% \end{syntax}
+% This is the low-level function for conversion of an
+% \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Calculates the value of the \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Calculates the value of the \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Calculates the value of the \meta{integer expression} 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{integer expression} \Arg{base}
+% \end{syntax}
+% Calculates the value of the \meta{integer expression} 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{integer expression}
+% \end{syntax}
+% Places the value of the \meta{integer expression} 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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Evaluates the two \meta{integer expressions} 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{intexpr}
+% \end{syntax}
+% Evaluates the \meta{integer expression} then produces a
+% pseudo-random number between $1$ and the \meta{intexpr} (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{integer expression}
+% \end{syntax}
+% Displays the result of evaluating the \meta{integer expression}
+% 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{integer expression}
+% \end{syntax}
+% Writes the result of evaluating the \meta{integer expression}
+% 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}{\c_max_int}
+% The maximum value that can be stored as an integer.
+% \end{variable}
+%
+% \begin{variable}{\c_max_register_int}
+% Maximum number of registers.
+% \end{variable}
+%
+% \begin{variable}{\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}
+%
+% \subsection{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}
+% These are both names for 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}
+%<*initex|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}
+%
+% \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.
+% When debugging, use parentheses to catch early termination.
+% \begin{macrocode}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_eval:n } }
+\cs_new:Npn \int_eval:n #1
+ { \int_value:w \@@_eval:w #1 \@@_eval_end: }
+\cs_new:Npn \int_eval:w { \int_value:w \@@_eval:w }
+% \end{macrocode}
+% \end{macro}
+% \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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_abs:n } }
+\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: }
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_max:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \int_max:nn }
+ }
+\cs_set: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:
+ }
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_min:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \int_min:nn }
+ }
+\cs_set: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}
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_div_truncate:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \int_div_truncate:nn }
+ }
+\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}
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_mod:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \int_mod:nn }
+ }
+\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}
+%<*package>
+\cs_new_protected:Npn \int_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newcount \cs_end: #1
+ }
+%</package>
+\cs_generate_variant:Nn \int_new:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_const:Nn, \int_const:cn}
+% \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}
+\__kernel_patch_args:nnnNNpn
+ { \__kernel_chk_var_scope:NN c #1 }
+ { }
+ { {#1} { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \int_const:Nn } }
+\cs_new_protected:Npn \int_const:Nn #1#2
+ {
+ \int_compare:nNnTF {#2} < \c_zero_int
+ {
+ \int_new:N #1
+ \tex_global:D
+ }
+ {
+ \int_compare:nNnTF {#2} > \c_@@_max_constdef_int
+ {
+ \int_new:N #1
+ \tex_global:D
+ }
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \@@_constdef:Nw
+ }
+ }
+ #1 = \@@_eval:w #2 \@@_eval_end:
+ }
+\cs_generate_variant:Nn \int_const:Nn { c }
+\if_int_odd:w 0
+ \cs_if_exist:NT \tex_luatexversion:D { 1 }
+ \cs_if_exist:NT \tex_disablecjktoken:D
+ { \if_int_compare:w \tex_jis:D "2121 = "3000 ~ 1 \fi: }
+ \cs_if_exist:NT \tex_XeTeXversion:D { 1 } ~
+ \cs_if_exist:NTF \tex_disablecjktoken:D
+ { \cs_new_eq:NN \@@_constdef:Nw \tex_kchardef:D }
+ { \cs_new_eq:NN \@@_constdef:Nw \tex_chardef:D }
+ \@@_constdef:Nw \c_@@_max_constdef_int 1114111 ~
+\else:
+ \cs_new_eq:NN \@@_constdef:Nw \tex_mathchardef:D
+ \tex_mathchardef:D \c_@@_max_constdef_int 32767 ~
+\fi:
+% \end{macrocode}
+% \end{variable}
+% \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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \int_zero:N #1 { #1 = \c_zero_int }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \int_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \int_set_eq:NN { c , Nc , cc }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+%
+% Several functions here have a signature |:Nn| and are such that when
+% debugging, the first argument should be checked to be a local/global
+% variable and the second should be wrapped in code for an expression.
+% The temporary function \cs{@@_tmp:w} finds the name |#3| of the
+% function being redefined and writes the appropriate patch.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \__kernel_patch_args:nnnNNpn
+ { #1 ##1 }
+ { }
+ { {##1} { \__kernel_chk_expr:nNnN {##2} \@@_eval:w { } #3 } }
+ #2 #3
+ }
+% \end{macrocode}
+%
+% \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. For each function,
+% the debugging code produced by \cs{@@_tmp:w} checks that the
+% assigned variable is correctly local/global and wraps the
+% expression in some checking code.
+% \begin{macrocode}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \int_add:Nn #1#2
+ { \tex_advance:D #1 by \@@_eval:w #2 \@@_eval_end: }
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \int_sub:Nn #1#2
+ { \tex_advance:D #1 by - \@@_eval:w #2 \@@_eval_end: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \int_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by \@@_eval:w #2 \@@_eval_end: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \int_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by - \@@_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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \int_incr:N #1
+ { \tex_advance:D #1 \c_one_int }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \int_decr:N #1
+ { \tex_advance:D #1 - \c_one_int }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \int_gincr:N #1
+ { \tex_global:D \tex_advance:D #1 \c_one_int }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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. Thus there is no need to check their
+% existence as for token list variables. However, the code that
+% checks whether the assignment is local or global is still needed.
+% \begin{macrocode}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \int_set:Nn #1#2
+ { #1 ~ \@@_eval:w #2 \@@_eval_end: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\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:
+% \begin{macrocode}
+\cs_new_eq:NN \int_use:N \tex_the:D
+% \end{macrocode}
+% We hand-code this for some speed gain:
+% \begin{macrocode}
+%\cs_generate_variant:Nn \int_use:N { c }
+\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 \q_stop
+ {
+ { }
+ \c_zero_int \fi:
+ \__kernel_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{q_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_ } \q_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 \q_stop
+ {
+ \exp_after:wN \@@_compare:NNw
+ \@@_to_roman:w - 0 #2 \q_mark
+ #1#2 \q_stop
+ }
+\cs_new:Npn \@@_compare:NNw #1#2#3 \q_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 \q_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_q_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}
+\__kernel_patch_conditional_args:nNNpnn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_compare:nNn }
+ { \@@_eval_end: #2 }
+ { \__kernel_chk_expr:nNnN {#3} \@@_eval:w { } \int_compare:nNn }
+ }
+\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}[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{tl_case:nn(TF)} as described in \pkg{l3tl}.
+% \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} { } \q_mark {#3} \q_mark {#4} \q_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 \q_mark #4#5 \q_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}
+\__kernel_patch_conditional_args:nNNpnn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_if_odd:n } }
+\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:
+ }
+\__kernel_patch_conditional_args:nNNpnn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_if_even:n } }
+\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}
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__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
+ }
+ }
+\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
+ {
+ \__kernel_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:Npx
+ { @@_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}
+% \UnitTested
+% Nothing exciting here.
+% \begin{macrocode}
+\cs_new_eq:NN \int_to_arabic:n \int_eval:n
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\int_to_symbols:nnn}
+% \UnitTested
+% \begin{macro}{\@@_to_symbols:nnnn}
+% 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}
+ {
+ \exp_args:NNo \exp_args:No \@@_to_symbols:nnnn
+ {
+ \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
+ }
+% \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{q_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{q_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 \q_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}
+ \q_stop { \@@_from_alph:nN { 0 } }
+ \q_recursion_tail \q_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_from_alph:nN #1#2
+ {
+ \quark_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}
+ \q_stop { \@@_from_base:nnN { 0 } {#2} }
+ \q_recursion_tail \q_recursion_stop
+ }
+ }
+\cs_new:Npn \@@_from_base:nnN #1#2#3
+ {
+ \quark_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
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \int_if_exist:cF { c_@@_from_roman_ #1 _int }
+ { \@@_from_roman_error:w }
+ \quark_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
+ { \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
+ { \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}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3intarray.dtx b/macros/latex/contrib/l3kernel/l3intarray.dtx
new file mode 100644
index 0000000000..a371b6ee63
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3intarray.dtx
@@ -0,0 +1,554 @@
+% \iffalse meta-comment
+%
+%% File: l3intarray.dtx
+%
+% Copyright (C) 2017-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3intarray} package: fast global integer arrays^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3intarray} 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.
+%
+% \begin{function}[added = 2018-03-29]{\intarray_new:Nn}
+% \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}[EXP, added = 2018-03-29]{\intarray_count:N}
+% \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}
+%
+% \begin{function}[added = 2018-03-29]{\intarray_gset:Nnn}
+% \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}
+%
+% \begin{function}[added = 2018-05-04]{\intarray_gzero:N}
+% \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}
+%
+% \begin{function}[EXP, added = 2018-03-29]{\intarray_item:Nn}
+% \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}
+% \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}
+%
+% \subsection{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} package
+% 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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=intarray>
+% \end{macrocode}
+%
+% \subsection{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}{\l_@@_loop_int}
+% A loop index.
+% \begin{macrocode}
+\int_new:N \l_@@_loop_int
+% \end{macrocode}
+% \end{variable}
+%
+% \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{macrocode}
+\__kernel_msg_new:nnn { kernel } { negative-array-size }
+ { Size~of~array~may~not~be~negative:~#1 }
+% \end{macrocode}
+%
+% \begin{macro}{\intarray_new:Nn, \@@_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 }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN g #1 } { }
+\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
+ {
+ \__kernel_msg_error:nnx { 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 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_count:N}
+% Size of an array.
+% \begin{macrocode}
+\cs_new:Npn \intarray_count:N #1 { \int_value:w \@@_count:w #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{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:NNn}
+% 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#4#5
+ {
+ \if_int_compare:w 1 > #3 \exp_stop_f:
+ \@@_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ \if_int_compare:w #3 > \intarray_count:N #2 \exp_stop_f:
+ \@@_bounds_error:NNn #1 #2 {#3}
+ #5
+ \else:
+ #4
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_bounds_error:NNn #1#2#3
+ {
+ #1 { kernel } { out-of-bounds }
+ { \token_to_str:N #2 } {#3} { \intarray_count:N #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\intarray_gset:Nnn, \__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_new_protected:Npn \@@_gset:Nww #1#2 ; #3 ;
+ {
+ \@@_bounds:NNnTF \__kernel_msg_error:nnxxx #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
+ {
+ \__kernel_msg_error:nnxxxx { 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}
+% 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
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\intarray_item:Nn, \__kernel_intarray_item:Nn}
+% \begin{macro}{\@@_item:Nn}
+% 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_new:Npn \@@_item:Nw #1#2 ;
+ {
+ \@@_bounds:NNnTF \__kernel_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}
+% 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 } } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Working with contents of integer arrays}
+%
+% At the time of writing these are candidates, but we need at least
+% \cs{intarray_const_from_clist:Nn} in \pkg{l3fp} so before
+% \pkg{l3candidates}.
+%
+% \begin{macro}{\intarray_const_from_clist:Nn, \@@_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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\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_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}
+%
+% \begin{macro}[rEXP]{\intarray_to_clist:N, \@@_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 \intarray_to_clist:N #1 { \@@_to_clist:Nn #1 { , } }
+\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}{\intarray_show:N, \intarray_log:N}
+% 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:nnxxxx }
+\cs_generate_variant:Nn \intarray_show:N { c }
+\cs_new_protected:Npn \intarray_log:N { \@@_show:NN \msg_log:nnxxxx }
+\cs_generate_variant:Nn \intarray_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { LaTeX/kernel } { show-intarray }
+ { \token_to_str:N #2 }
+ { \intarray_count:N #2 }
+ { >~ \@@_to_clist:Nn #2 { , ~ } }
+ { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Random arrays}
+%
+% \begin{macro}
+% {
+% \intarray_gset_rand:Nn,
+% \intarray_gset_rand:Nnn,
+% \@@_gset_rand:Nnn,
+% \@@_gset_rand:Nff,
+% \@@_gset_rand_auxi:Nnnn,
+% \@@_gset_rand_auxii:Nnnn,
+% \@@_gset_rand_auxiii:Nnnn,
+% \@@_gset_all_same:Nn,
+% }
+% We only perform the bounds checks once. This is done by two
+% \cs{@@_gset_overflow_test:nw}, with an appropriate empty argument to
+% avoid a spurious \enquote{at position \texttt{\#1}} part in the
+% error message. Then calculate the number of choices: this is at
+% most $(2^{30}-1)-(-(2^{30}-1))+1=2^{31}-1$, which just barely does
+% not overflow. For small ranges use \cs{__kernel_randint:n} (making
+% sure to subtract~$1$ \emph{before} adding the random number to the
+% \meta{min}, to avoid overflow when \meta{min} or \meta{max} are
+% $\pm\cs{c_max_int}$), otherwise \cs{__kernel_randint:nn}. Finally,
+% if there are no random numbers do not define any of the auxiliaries.
+% \begin{macrocode}
+\cs_new_protected:Npn \intarray_gset_rand:Nn #1
+ { \intarray_gset_rand:Nnn #1 { 1 } }
+\sys_if_rand_exist:TF
+ {
+ \cs_new_protected:Npn \intarray_gset_rand:Nnn #1#2#3
+ {
+ \@@_gset_rand:Nff #1
+ { \int_eval:n {#2} } { \int_eval:n {#3} }
+ }
+ \cs_new_protected:Npn \@@_gset_rand:Nnn #1#2#3
+ {
+ \int_compare:nNnTF {#2} > {#3}
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { randint-backward-range } {#2} {#3}
+ \@@_gset_rand:Nnn #1 {#3} {#2}
+ }
+ {
+ \@@_gset_overflow_test:nw {#2}
+ \@@_gset_rand_auxi:Nnnn #1 { } {#2} {#3}
+ }
+ }
+ \cs_generate_variant:Nn \@@_gset_rand:Nnn { Nff }
+ \cs_new_protected:Npn \@@_gset_rand_auxi:Nnnn #1#2#3#4
+ {
+ \@@_gset_overflow_test:nw {#4}
+ \@@_gset_rand_auxii:Nnnn #1 { } {#4} {#3}
+ }
+ \cs_new_protected:Npn \@@_gset_rand_auxii:Nnnn #1#2#3#4
+ {
+ \exp_args:NNf \@@_gset_rand_auxiii:Nnnn #1
+ { \int_eval:n { #3 - #4 + 1 } } {#4} {#3}
+ }
+ \cs_new_protected:Npn \@@_gset_rand_auxiii:Nnnn #1#2#3#4
+ {
+ \exp_args:NNf \@@_gset_all_same:Nn #1
+ {
+ \int_compare:nNnTF {#2} > \c__kernel_randint_max_int
+ {
+ \exp_stop_f:
+ \int_eval:n { \__kernel_randint:nn {#3} {#4} }
+ }
+ {
+ \exp_stop_f:
+ \int_eval:n { \__kernel_randint:n {#2} - 1 + #3 }
+ }
+ }
+ }
+ \cs_new_protected:Npn \@@_gset_all_same:Nn #1#2
+ {
+ \int_zero:N \l_@@_loop_int
+ \prg_replicate:nn { \intarray_count:N #1 }
+ {
+ \int_incr:N \l_@@_loop_int
+ \__kernel_intarray_gset:Nnn #1 \l_@@_loop_int {#2}
+ }
+ }
+ }
+ {
+ \cs_new_protected:Npn \intarray_gset_rand:Nnn #1#2#3
+ {
+ \__kernel_msg_error:nnn { kernel } { fp-no-random }
+ { \intarray_gset_rand:Nnn #1 {#2} {#3} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3kernel-functions.dtx b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx
new file mode 100644
index 0000000000..e8d675eeba
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx
@@ -0,0 +1,642 @@
+% \iffalse meta-comment
+%
+%% File: l3kernel-functions.dtx
+%
+% Copyright (C) 2018-2019 The LaTeX3 project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Kernel-reserved functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{Internal kernel functions}
+%
+% \begin{function}{\__kernel_chk_cs_exist:N, \__kernel_chk_cs_exist:c}
+% \begin{syntax}
+% \cs{__kernel_chk_cs_exist:N} \meta{cs}
+% \end{syntax}
+% This function is only created if debugging is enabled. It checks
+% that \meta{cs} exists according to the criteria for
+% \cs{cs_if_exist_p:N}, and if not raises a kernel-level error.
+% \end{function}
+%
+% \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_var_exist:N}
+% \begin{syntax}
+% \cs{__kernel_chk_var_exist:N} \meta{var}
+% \end{syntax}
+% This function is only created if debugging is enabled. It checks
+% that \meta{var} is defined according to the criteria for
+% \cs{cs_if_exist_p:N}, and if not raises a kernel-level error.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_var_scope:NN}
+% \begin{syntax}
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}
+% \end{syntax}
+% Checks the \meta{var} has the correct \meta{scope}, and if not
+% raises a kernel-level error. This function is only created if
+% debugging is enabled. The \meta{scope} is a single letter |l|, |g|,
+% |c| denoting local variables, global variables, or constants. More
+% precisely, if the variable name starts with a letter and an
+% underscore (normal \pkg{expl3} convention) the function checks that
+% this single letter matches the \meta{scope}. Otherwise the function
+% cannot know the scope \meta{var} the first time: instead, it defines
+% |\__debug_chk_/|\meta{var name} to store that information for the
+% next call. Thus, if a given \meta{var} is subject to assignments of
+% different scopes a kernel error will result.
+% \end{function}
+%
+% \begin{function}{\__kernel_chk_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}, and assuming that is
+% true applies
+% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}, where
+% \meta{scope} is |l| or~|g|.
+% \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_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:} and \cs{debug_off:}.
+% \end{function}
+%
+% \begin{function}{\__kernel_if_debug:TF}
+% \begin{syntax}
+% \cs{__kernel_if_debug:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Runs the \meta{true code} if debugging is enabled, namely only in
+% \LaTeXe{} package mode with one of the options
+% \texttt{check-declarations}, \texttt{enable-debug}, or
+% \texttt{log-functions}. Otherwise runs the \meta{false code}. The
+% |T| and |F| variants are not provided for this low-level
+% conditional.
+% \end{function}
+%
+% \begin{function}{\__kernel_debug_log:x}
+% \begin{syntax}
+% \cs{__kernel_debug_log:x} \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:x}.
+% Otherwise, the \meta{message text} is ignored using \cs{use_none:n}.
+% This function is only created if debugging is enabled.
+% \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{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:nN} then
+% produces an error message indicating that that file was not found.
+% \end{function}
+%
+% \begin{function}{\__kernel_file_name_sanitize:nN}
+% \begin{syntax}
+% \cs{__kernel_file_name_sanitize:nN} \Arg{name} \meta{str var}
+% \end{syntax}
+% For converting a \meta{name} to a string where active characters are treated
+% as strings.
+% \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}{\__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{file_get_full_name: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{function}
+% {\__kernel_msg_new:nnnn, \__kernel_msg_new:nnn}
+% \begin{syntax}
+% \cs{__kernel_msg_new:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text}
+% \end{syntax}
+% Creates a kernel \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 and expanded at the time the message is used.
+% An error is raised if the \meta{message} already exists.
+% \end{function}
+%
+% \begin{function}
+% {\__kernel_msg_set:nnnn, \__kernel_msg_set:nnn}
+% \begin{syntax}
+% \cs{__kernel_msg_set:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text}
+% \end{syntax}
+% Sets up the text for a kernel \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 and expanded at the time the message is used.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_msg_fatal:nnnnnn ,
+% \__kernel_msg_fatal:nnnnn ,
+% \__kernel_msg_fatal:nnnn ,
+% \__kernel_msg_fatal:nnn ,
+% \__kernel_msg_fatal:nn ,
+% \__kernel_msg_fatal:nnxxxx ,
+% \__kernel_msg_fatal:nnxxx ,
+% \__kernel_msg_fatal:nnxx ,
+% \__kernel_msg_fatal:nnx
+% }
+% \begin{syntax}
+% \cs{__kernel_msg_fatal:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues kernel \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. Cannot be redirected.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_msg_error:nnnnnn ,
+% \__kernel_msg_error:nnnnn ,
+% \__kernel_msg_error:nnnn ,
+% \__kernel_msg_error:nnn ,
+% \__kernel_msg_error:nn ,
+% \__kernel_msg_error:nnxxxx ,
+% \__kernel_msg_error:nnxxx ,
+% \__kernel_msg_error:nnxx ,
+% \__kernel_msg_error:nnx
+% }
+% \begin{syntax}
+% \cs{__kernel_msg_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues kernel \meta{module} error \meta{message}, passing \meta{arg one}
+% to
+% \meta{arg four} to the text-creating functions. The error
+% stops processing and issues the text at the terminal. After user input,
+% the run continues. Cannot be redirected.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_msg_warning:nnnnnn ,
+% \__kernel_msg_warning:nnnnn ,
+% \__kernel_msg_warning:nnnn ,
+% \__kernel_msg_warning:nnn ,
+% \__kernel_msg_warning:nn ,
+% \__kernel_msg_warning:nnxxxx ,
+% \__kernel_msg_warning:nnxxx ,
+% \__kernel_msg_warning:nnxx ,
+% \__kernel_msg_warning:nnx
+% }
+% \begin{syntax}
+% \cs{__kernel_msg_warning:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues kernel \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, but the \TeX{} run is not interrupted.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__kernel_msg_info:nnnnnn ,
+% \__kernel_msg_info:nnnnn ,
+% \__kernel_msg_info:nnnn ,
+% \__kernel_msg_info:nnn ,
+% \__kernel_msg_info:nn ,
+% \__kernel_msg_info:nnxxxx ,
+% \__kernel_msg_info:nnxxx ,
+% \__kernel_msg_info:nnxx ,
+% \__kernel_msg_info:nnx
+% }
+% \begin{syntax}
+% \cs{__kernel_msg_info:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues kernel \meta{module} information \meta{message}, passing
+% \meta{arg one} to \meta{arg four} to the text-creating functions.
+% The information text is added to the log file.
+% \end{function}
+%
+% \begin{function}[EXP]
+% {
+% \__kernel_msg_expandable_error:nnnnnn,
+% \__kernel_msg_expandable_error:nnnnn,
+% \__kernel_msg_expandable_error:nnnn,
+% \__kernel_msg_expandable_error:nnn,
+% \__kernel_msg_expandable_error:nn,
+% \__kernel_msg_expandable_error:nnffff,
+% \__kernel_msg_expandable_error:nnfff,
+% \__kernel_msg_expandable_error:nnff,
+% \__kernel_msg_expandable_error:nnf,
+% }
+% \begin{syntax}
+% \cs{__kernel_msg_expandable_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues an error, passing \meta{arg one} to \meta{arg four}
+% to the text-creating functions. The resulting string must
+% be much shorter than a line, otherwise it is cropped.
+% \end{function}
+%
+% \begin{function}{\__kernel_patch:nnNNpn}
+% \begin{syntax}
+% \cs{__kernel_patch:nnNNpn} \Arg{before} \Arg{after}
+% \meta{definition} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% If debugging is not enabled, this function ignores the \meta{before}
+% and \meta{after} code and performs the \meta{definition} with no
+% patching. Otherwise it replaces \meta{code} by \meta{before}
+% \meta{code} \meta{after} (which can involve |#1| and so on) in the
+% \meta{definition} that follows. The \meta{definition} must start
+% with \cs{cs_new:Npn} or \cs{cs_set:Npn} or \cs{cs_gset:Npn} or their
+% \texttt{_protected} counterparts. Other cases can be added as
+% needed.
+% \end{function}
+%
+% \begin{function}{\__kernel_patch_conditional:nNNpnn}
+% \begin{syntax}
+% \cs{__kernel_patch_conditional:nNNpnn} \Arg{before}
+% \meta{definition} \meta{conditional} \meta{parameters} \Arg{type} \Arg{code}
+% \end{syntax}
+% Similar to \cs{__kernel_patch:nnNNpn} for conditionals, namely
+% \meta{definition} must be \cs{prg_new_conditional:Npnn} or its
+% \texttt{_protected} counterpart. There is no \meta{after} code
+% because that would interfere with the action of the conditional.
+% \end{function}
+%
+% \begin{function}
+% {\__kernel_patch_args:nNNpn, \__kernel_patch_conditional_args:nNNpnn}
+% \begin{syntax}
+% \cs{__kernel_patch_args:nNNpn} \Arg{arguments}
+% \meta{definition} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Like \cs{__kernel_patch:nnNNpn}, this tweaks the following
+% definition, but from the \enquote{inside out} (and if debugging is
+% not enabled, the \meta{arguments} are ignored). It replaces |#1|,
+% |#2| and so on in the \meta{code} of the definition as indicated by
+% the \meta{arguments}. More precisely, a temporary function is
+% defined using the \meta{definition} with the \meta{parameters} and
+% \meta{code}, then the result of expanding that function once in
+% front of the \meta{arguments} is used instead of the \meta{code}
+% when defining the actual function. For instance,
+% \begin{quote}
+% \cs{__kernel_patch_args:nNNpn} |{ { (#1) } }| \\
+% \cs{cs_new:Npn} \cs{int_eval:n} |#1| \\
+% ~~|{ \int_value:w \__int_eval:w #1 \__int_eval_end: }|
+% \end{quote}
+% would replace |#1| by |(#1)| in the definition of \cs{int_eval:n} when
+% debugging is enabled. This fails if the \meta{code} contains~|##|.
+% The \cs{__kernel_patch_conditional_args:nNNpnn} function is for use
+% before \cs{prg_new_conditional:Npnn} or its \texttt{_protected}
+% counterpart.
+% \end{function}
+%
+% \begin{function}
+% {\__kernel_patch_args:nnnNNpn, \__kernel_patch_conditional_args:nnnNNpnn}
+% \begin{syntax}
+% \cs{__kernel_patch_args:nnnNNpn} \Arg{before} \Arg{after} \Arg{arguments}
+% \meta{definition} \meta{function} \meta{parameters} \Arg{code}
+% \end{syntax}
+% A combination of \cs{__kernel_patch:nnNNpn} and
+% \cs{__kernel_patch_args:nNNpn}.
+% \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{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}
+%
+% \section{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:x
+% }
+% \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:x
+% }
+% \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:x
+% }
+% \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:x
+% }
+% \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:x
+% }
+% \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_postscript_header:n}
+% \begin{syntax}
+% \cs{__kernel_backend_postscript_header:n} \Arg{PostScript}
+% \end{syntax}
+% Adds the \meta{PostScript} to the PostScript header.
+% \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:x
+% }
+% \begin{syntax}
+% \cs{__kernel_backend_matrix:n} \Arg{matrix}
+% \end{syntax}
+% Applies the \meta{matrix} to the current transformation matrix.
+% \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/macros/latex/contrib/l3kernel/l3keys.dtx b/macros/latex/contrib/l3kernel/l3keys.dtx
new file mode 100644
index 0000000000..074ca85b6e
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3keys.dtx
@@ -0,0 +1,2831 @@
+% \iffalse meta-comment
+%
+%% File: l3keys.dtx
+%
+% Copyright (C) 2006-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Key--value interfaces^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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}
+%
+% 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 logical
+% groups. 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}
+% \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 with the exception of the special |.undefine:| property, all
+% key properties define the key within the current \TeX{} scope.
+%
+% \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}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean} to \meta{value} (which
+% must be either \texttt{true} or \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}
+% \end{syntax}
+% Defines \meta{key} to set \meta{boolean} to the logical
+% inverse of \meta{value} (which must be either \texttt{true} or
+% \texttt{false}).
+% If the \meta{boolean} 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:on, .choices:xn}
+% \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}[updated = 2013-07-09]
+% {.default:n, .default:V, .default:o, .default:x}
+% \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.
+% \end{function}
+%
+% \begin{function}{.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.
+% \end{function}
+%
+% \begin{function}{.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.
+% \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} declared. Groups
+% provide a \enquote{secondary axis} for selectively setting keys, and are
+% described in Section~\ref{sec:l3keys:selective}.
+% \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 \meta{parents}. 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}
+% \end{function}
+%
+% \begin{function}[updated = 2013-07-09]
+% {.initial:n, .initial:V, .initial:o, .initial:x}
+% \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}{.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.
+% \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:on, .multichoices:xn}
+% \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]
+% {.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.
+% \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}{.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.
+% \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}{.tl_set_x:N, .tl_set_x:c, .tl_gset_x:N, .tl_gset_x:c}
+% \begin{syntax}
+% \meta{key} .tl_set_x: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{x}-type expansion
+% (\emph{i.e.}~using \cs{tl_set:Nx}). 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 scope.
+% \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 \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 \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 sub-groups for a given module. This can be achieved
+% either by adding a sub-division to the module name:
+% \begin{verbatim}
+% \keys_define:nn { mymodule / subgroup }
+% { key .code:n = code }
+% \end{verbatim}
+% or to the key name:
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% { subgroup / 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/subgroup/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:nnxxx { 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{Setting keys}
+%
+% \begin{function}[updated = 2017-11-14]
+% {\keys_set:nn, \keys_set:nV, \keys_set:nv, \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 = 2015-07-14]
+% {\l_keys_key_tl, \l_keys_path_tl, \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 three token list variables. These may be used within
+% the code of the key.
+%
+% 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}.
+%
+% 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_tl}, and will have been processed by \cs{tl_to_str:n}.
+%
+% 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_tl}, and will have been processed by
+% \cs{tl_to_str:n}.
+% \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.
+% \begin{verbatim}
+% \keys_define:nn { mymodule }
+% {
+% unknown .code:n =
+% You~tried~to~set~key~'\l_keys_key_tl'~to~'#1'.
+% }
+% \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:no,
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:noN,
+% \keys_set_known:nnnN, \keys_set_known:nVnN,
+% \keys_set_known:nvnN, \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 = 2013-07-14, updated = 2019-01-29]
+% {
+% \keys_set_filter:nnn, \keys_set_filter:nnV,
+% \keys_set_filter:nnv, \keys_set_filter:nno,
+% \keys_set_filter:nnnN, \keys_set_filter:nnVN,
+% \keys_set_filter:nnvN, \keys_set_filter:nnoN,
+% \keys_set_filter:nnnnN, \keys_set_filter:nnVnN,
+% \keys_set_filter:nnvnN, \keys_set_filter:nnonN,
+% }
+% \begin{syntax}
+% \cs{keys_set_filter:nnn} \Arg{module} \Arg{groups} \Arg{keyval list}
+% \cs{keys_set_filter:nnnN} \Arg{module} \Arg{groups} \Arg{keyval list} \meta{tl}
+% \cs{keys_set_filter:nnnnN} \Arg{module} \Arg{groups} \Arg{keyval list} \meta{root} \meta{tl}
+% \end{syntax}
+% Activates key filtering in an \enquote{opt-out} sense: keys assigned to any
+% of the \meta{groups} specified are ignored. 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_filter:nnn} version skips this stage.
+%
+% Use of \cs{keys_set_filter: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{Utility functions for keys}
+%
+% \begin{function}[EXP, pTF, updated = 2017-11-14]{\keys_if_exist:nn}
+% \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}[updated = 2011-09-08]{\keyval_parse:NNn}
+% \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.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3keys} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \subsection{Low-level interface}
+%
+% The low-level key parser is based heavily on \pkg{keyval}, but with a number
+% of additional \enquote{safety} requirements and with the idea that the
+% parsed list of key--value pairs can be processed in a variety of ways.
+% The net result is that this code needs around twice 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}{\l_@@_key_tl, \l_@@_value_tl}
+% The current key name and value.
+% \begin{macrocode}
+\tl_new:N \l_@@_key_tl
+\tl_new:N \l_@@_value_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_sanitise_tl}
+% A token list variable for dealing with awkward category codes in the
+% input.
+% \begin{macrocode}
+\tl_new:N \l_@@_sanitise_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\keyval_parse:NNn}
+% The main function starts off by normalising category codes in package mode.
+% That's relatively \enquote{expensive} so is skipped (hopefully) in format
+% mode. We then hand off to the parser. The use of \cs{q_mark} here prevents
+% loss of braces from the key argument. Notice that by passing the two
+% processor commands along the input stack we avoid the need to track these
+% at all.
+% \begin{macrocode}
+\cs_new_protected:Npn \keyval_parse:NNn #1#2#3
+ {
+%<*initex>
+ \@@_loop:NNw #1#2 \q_mark #3 , \q_recursion_tail ,
+%</initex>
+%<*package>
+ \tl_set:Nn \l_@@_sanitise_tl {#3}
+ \@@_sanitise_equals:
+ \@@_sanitise_comma:
+ \exp_after:wN \@@_loop:NNw \exp_after:wN #1 \exp_after:wN #2
+ \exp_after:wN \q_mark \l_@@_sanitise_tl , \q_recursion_tail ,
+%</package>
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_sanitise_equals:, \@@_sanitise_comma:}
+% \begin{macro}
+% {
+% \@@_sanitise_equals_auxi:w, \@@_sanitise_equals_auxii:w,
+% \@@_sanitise_comma_auxi:w, \@@_sanitise_comma_auxii:w,
+% \@@_sanitise_aux:w
+% }
+% A reasonably fast search and replace set up specifically for the active
+% tokens. The nature of the input is known so everything is hard-coded.
+% With only two tokens to cover, the speed gain from using dedicated
+% functions is worth it.
+% \begin{macrocode}
+%<*package>
+\group_begin:
+ \char_set_catcode_active:n { `\= }
+ \char_set_catcode_active:n { `\, }
+ \cs_new_protected:Npn \@@_sanitise_equals:
+ {
+ \exp_after:wN \@@_sanitise_equals_auxi:w \l_@@_sanitise_tl
+ \q_mark = \q_nil =
+ \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl
+ }
+ \cs_new_protected:Npn \@@_sanitise_equals_auxi:w #1 =
+ {
+ \tl_set:Nn \l_@@_sanitise_tl {#1}
+ \@@_sanitise_equals_auxii:w
+ }
+ \cs_new_protected:Npn \@@_sanitise_equals_auxii:w #1 =
+ {
+ \if_meaning:w \q_nil #1 \scan_stop:
+ \else:
+ \tl_set:Nx \l_@@_sanitise_tl
+ {
+ \exp_not:o \l_@@_sanitise_tl
+ \token_to_str:N =
+ \exp_not:n {#1}
+ }
+ \exp_after:wN \@@_sanitise_equals_auxii:w
+ \fi:
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma:
+ {
+ \exp_after:wN \@@_sanitise_comma_auxi:w \l_@@_sanitise_tl
+ \q_mark , \q_nil ,
+ \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma_auxi:w #1 ,
+ {
+ \tl_set:Nn \l_@@_sanitise_tl {#1}
+ \@@_sanitise_comma_auxii:w
+ }
+ \cs_new_protected:Npn \@@_sanitise_comma_auxii:w #1 ,
+ {
+ \if_meaning:w \q_nil #1 \scan_stop:
+ \else:
+ \tl_set:Nx \l_@@_sanitise_tl
+ {
+ \exp_not:o \l_@@_sanitise_tl
+ \token_to_str:N ,
+ \exp_not:n {#1}
+ }
+ \exp_after:wN \@@_sanitise_comma_auxii:w
+ \fi:
+ }
+\group_end:
+\cs_new_protected:Npn \@@_sanitise_aux:w #1 \q_mark
+ { \tl_set:Nn \l_@@_sanitise_tl {#1} }
+%</package>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_loop:NNw}
+% A fast test for the end of the loop, remembering to remove the leading
+% quark first. Assuming that is not the case, look for a key and value then
+% loop around, re-inserting a leading quark in front of the next position.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_loop:NNw #1#2#3 ,
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_recursion_tail
+ \use_none:n #3 \prg_do_nothing:
+ \else:
+ \@@_split:NNw #1#2#3 == \q_stop
+ \exp_after:wN \@@_loop:NNw \exp_after:wN #1 \exp_after:wN #2
+ \exp_after:wN \q_mark
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_split:NNw, \@@_split_value:NNw}
+% \begin{macro}{\@@_split_tidy:w}
+% \begin{macro}{\@@_action:}
+% The value is picked up separately from the key so there can be another
+% quark inserted at the front, keeping braces and allowing both parts to
+% share the same code paths. The key is found first then there's a check
+% that there is something there: this is biased to the common case of there
+% actually being a key. For the value, we first need to see if there is
+% anything to do: if there is, extract it. The appropriate action is then
+% inserted in front of the key and value. Doing this using an assignment is
+% marginally faster than an an expansion chain.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_split:NNw #1#2#3 =
+ {
+ \@@_def:Nn \l_@@_key_tl {#3}
+ \if_meaning:w \l_@@_key_tl \c_empty_tl
+ \exp_after:wN \@@_split_tidy:w
+ \else:
+ \exp_after:wN \@@_split_value:NNw
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN \q_mark
+ \fi:
+ }
+\cs_new_protected:Npn \@@_split_value:NNw #1#2#3 = #4 \q_stop
+ {
+ \if:w \scan_stop: \tl_to_str:n {#4} \scan_stop:
+ \cs_set:Npx \@@_action:
+ { \exp_not:N #1 { \exp_not:o \l_@@_key_tl } }
+ \else:
+ \if:w
+ \scan_stop:
+ \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #4 }
+ \scan_stop:
+ \@@_def:Nn \l_@@_value_tl {#3}
+ \cs_set:Npx \@@_action:
+ {
+ \exp_not:N #2
+ { \exp_not:o \l_@@_key_tl }
+ { \exp_not:o \l_@@_value_tl }
+ }
+ \else:
+ \cs_set:Npn \@@_action:
+ {
+ \__kernel_msg_error:nn { kernel }
+ { misplaced-equals-sign }
+ }
+ \fi:
+ \fi:
+ \@@_action:
+ }
+\cs_new_protected:Npn \@@_split_tidy:w #1 \q_stop
+ {
+ \if:w
+ \scan_stop:
+ \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #1 }
+ \scan_stop:
+ \else:
+ \exp_after:wN \@@_empty_key:
+ \fi:
+ }
+\cs_new:Npn \@@_action: { }
+\cs_new_protected:Npn \@@_empty_key:
+ { \__kernel_msg_error:nn { kernel } { misplaced-equals-sign } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_def:Nn}
+% \begin{macro}[EXP]{\@@_def_aux:n}
+% \begin{macro}[EXP]{\@@_def_aux:w}
+% First remove the leading quark, then trim spaces off, and finally remove
+% a set of braces.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_def:Nn #1#2
+ {
+ \tl_set:Nx #1
+ { \tl_trim_spaces_apply:oN { \use_none:n #2 } \@@_def_aux:n }
+ }
+\cs_new:Npn \@@_def_aux:n #1
+ { \@@_def_aux:w #1 \q_stop }
+\cs_new:Npn \@@_def_aux:w #1 \q_stop { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% One message for the low level parsing system.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { misplaced-equals-sign }
+ { Misplaced~equals~sign~in~key-value~input~\msg_line_number: }
+ {
+ LaTeX~is~attempting~to~parse~some~key-value~input~but~found~
+ two~equals~signs~not~separated~by~a~comma.
+ }
+% \end{macrocode}
+%
+% \subsection{Constants and variables}
+%
+% \begin{macrocode}
+%<@@=keys>
+% \end{macrocode}
+%
+% \begin{variable}
+% {
+% \c_@@_code_root_tl ,
+% \c_@@_default_root_tl ,
+% \c_@@_groups_root_tl ,
+% \c_@@_inherit_root_tl ,
+% \c_@@_type_root_tl ,
+% \c_@@_validate_root_tl
+% }
+% Various storage areas for the different data which make up keys.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_code_root_tl { key~code~>~ }
+\tl_const:Nn \c_@@_default_root_tl { key~default~>~ }
+\tl_const:Nn \c_@@_groups_root_tl { key~groups~>~ }
+\tl_const:Nn \c_@@_inherit_root_tl { key~inherit~>~ }
+\tl_const:Nn \c_@@_type_root_tl { key~type~>~ }
+\tl_const:Nn \c_@@_validate_root_tl { key~validate~>~ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_props_root_tl}
+% The prefix for storing properties.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_props_root_tl { 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_keys_key_tl}
+% The name of a key itself: needed when setting keys.
+% \begin{macrocode}
+\tl_new:N \l_keys_key_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_module_tl}
+% The module for an entire set of keys.
+% \begin{macrocode}
+\tl_new:N \l_@@_module_tl
+% \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_tl}
+% The \enquote{path} of the current key is stored here: this is
+% available to the programmer and so is public.
+% \begin{macrocode}
+\tl_new:N \l_keys_path_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_inherit_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_inherit_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_relative_tl}
+% The relative path for passing keys back to the user.
+% \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_tl}
+% The \enquote{property} begin set for a key at definition time is
+% stored here.
+% \begin{macrocode}
+\tl_new:N \l_@@_property_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_selective_bool, \l_@@_filtered_bool}
+% Two flags 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}
+\tl_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}
+%
+% \subsection{The key defining mechanism}
+%
+% \begin{macro}{\keys_define:nn}
+% \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_tl }
+\cs_new_protected:Npn \@@_define:nnn #1#2#3
+ {
+ \tl_set:Nx \l_@@_module_tl { \@@_trim_spaces:n {#2} }
+ \keyval_parse:NNn \@@_define:n \@@_define:nn {#3}
+ \tl_set:Nn \l_@@_module_tl {#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_tl \l_@@_property_tl }
+ { \@@_define_code:n {#2}
+ }
+ {
+ \tl_if_empty:NF \l_@@_property_tl
+ {
+ \__kernel_msg_error:nnxx { kernel } { key-property-unknown }
+ { \l_@@_property_tl } { \l_keys_path_tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_property_find:n}
+% \begin{macro}{\@@_property_find:w}
+% Searching for a property means finding the last |.| in the input,
+% and storing the text before and after it. Everything is turned into
+% strings, so there is no problem using an \texttt{x}-type expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_property_find:n #1
+ {
+ \tl_set:Nx \l_@@_property_tl { \@@_trim_spaces:n {#1} }
+ \exp_after:wN \@@_property_find:w \l_@@_property_tl . .
+ \q_stop {#1}
+ }
+\cs_new_protected:Npn \@@_property_find:w #1 . #2 . #3 \q_stop #4
+ {
+ \tl_if_blank:nTF {#3}
+ {
+ \tl_clear:N \l_@@_property_tl
+ \__kernel_msg_error:nnn { kernel } { key-no-property } {#4}
+ }
+ {
+ \str_if_eq:nnTF {#3} { . }
+ {
+ \tl_set:Nx \l_keys_path_tl
+ {
+ \tl_if_empty:NF \l_@@_module_tl
+ { \l_@@_module_tl / }
+ \tl_trim_spaces:n {#1}
+ }
+ \tl_set:Nn \l_@@_property_tl { . #2 }
+ }
+ {
+ \tl_set:Nx \l_keys_path_tl { \l_@@_module_tl / #1 . #2 }
+ \@@_property_search:w #3 \q_stop
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_property_search:w #1 . #2 \q_stop
+ {
+ \str_if_eq:nnTF {#2} { . }
+ {
+ \tl_set:Nx \l_keys_path_tl { \l_keys_path_tl }
+ \tl_set:Nn \l_@@_property_tl { . #1 }
+ }
+ {
+ \tl_set:Nx \l_keys_path_tl { \l_keys_path_tl . #1 }
+ \@@_property_search:w #2 \q_stop
+ }
+ }
+% \end{macrocode}
+% \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_tl \q_stop
+ { \use:c { \c_@@_props_root_tl \l_@@_property_tl } }
+ {
+ \__kernel_msg_error:nnxx { kernel }
+ { key-property-requires-value } { \l_@@_property_tl }
+ { \l_keys_path_tl }
+ }
+ }
+ { \use:c { \c_@@_props_root_tl \l_@@_property_tl } {#1} }
+ }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_define_code:w #1 \c_colon_str #2 \q_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}
+% 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_if_exist:NF #1 { \bool_new:N #1 }
+ \@@_choice_make:
+ \@@_cmd_set:nx { \l_keys_path_tl / true }
+ { \exp_not:c { bool_ #2 set_true:N } \exp_not:N #1 }
+ \@@_cmd_set:nx { \l_keys_path_tl / false }
+ { \exp_not:c { bool_ #2 set_false:N } \exp_not:N #1 }
+ \@@_cmd_set:nn { \l_keys_path_tl / unknown }
+ {
+ \__kernel_msg_error:nnx { kernel } { boolean-values-only }
+ { \l_keys_key_tl }
+ }
+ \@@_default_set:n { true }
+ }
+\cs_generate_variant:Nn \@@_bool_set:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_bool_set_inverse:Nn, \@@_bool_set_inverse:cn}
+% Inverse boolean setting is much the same.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_bool_set_inverse:Nn #1#2
+ {
+ \bool_if_exist:NF #1 { \bool_new:N #1 }
+ \@@_choice_make:
+ \@@_cmd_set:nx { \l_keys_path_tl / true }
+ { \exp_not:c { bool_ #2 set_false:N } \exp_not:N #1 }
+ \@@_cmd_set:nx { \l_keys_path_tl / false }
+ { \exp_not:c { bool_ #2 set_true:N } \exp_not:N #1 }
+ \@@_cmd_set:nn { \l_keys_path_tl / unknown }
+ {
+ \__kernel_msg_error:nnx { kernel } { boolean-values-only }
+ { \l_keys_key_tl }
+ }
+ \@@_default_set:n { true }
+ }
+\cs_generate_variant:Nn \@@_bool_set_inverse:Nn { c }
+% \end{macrocode}
+% \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_tl \@@_parent:o \l_keys_path_tl }
+ {
+ \str_if_eq:vnTF
+ { \c_@@_type_root_tl \@@_parent:o \l_keys_path_tl }
+ { choice }
+ {
+ \__kernel_msg_error:nnxx { kernel } { nested-choice-key }
+ { \l_keys_path_tl } { \@@_parent:o \l_keys_path_tl }
+ }
+ { \@@_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_tl \l_keys_path_tl }
+ { choice }
+ \@@_cmd_set:nn { \l_keys_path_tl } { #1 {##1} }
+ \@@_cmd_set:nn { \l_keys_path_tl / unknown }
+ {
+ \__kernel_msg_error:nnxx { kernel } { key-choice-unknown }
+ { \l_keys_path_tl } {##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:nx
+ { \l_keys_path_tl / \@@_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:nx, \@@_cmd_set:Vn, \@@_cmd_set:Vo}
+% Setting the code for a key first logs if appropriate that we are
+% defining a new key, then saves the code.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn
+ {
+ \cs_if_exist:cF { \c_@@_code_root_tl #1 }
+ { \__kernel_debug_log:x { Defining~key~#1~\msg_line_context: } }
+ }
+ { }
+\cs_new_protected:Npn \@@_cmd_set:nn #1#2
+ { \cs_set_protected:cpn { \c_@@_code_root_tl #1 } ##1 {#2} }
+\cs_generate_variant:Nn \@@_cmd_set:nn { nx , Vn , Vo }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_default_set:n}
+% Setting a default value is easy. These are stored using \cs{cs_set:cpx} 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_set_eq:cN
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ \tex_undefined:D
+ }
+ {
+ \cs_set_nopar:cpx
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ { \exp_not:n {#1} }
+ }
+ }
+% \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:Nn \l_@@_groups_clist {#1}
+ \clist_if_empty:NTF \l_@@_groups_clist
+ {
+ \cs_set_eq:cN { \c_@@_groups_root_tl \l_keys_path_tl }
+ \tex_undefined:D
+ }
+ {
+ \cs_set_eq:cN { \c_@@_groups_root_tl \l_keys_path_tl }
+ \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:
+ \cs_set_nopar:cpn { \c_@@_inherit_root_tl \l_keys_path_tl } {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_initialise:n}
+% A set up for initialisation: just run the code if it exists.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_initialise:n #1
+ {
+ \cs_if_exist_use:cT { \c_@@_code_root_tl \l_keys_path_tl } { {#1} }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_meta_make:n}
+% \begin{macro}{\@@_meta_make:nn}
+% To create a meta-key, simply set up to pass data through.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_meta_make:n #1
+ {
+ \@@_cmd_set:Vo \l_keys_path_tl
+ {
+ \exp_after:wN \keys_set:nn
+ \exp_after:wN { \l_@@_module_tl } {#1}
+ }
+ }
+\cs_new_protected:Npn \@@_meta_make:nn #1#2
+ { \@@_cmd_set:Vn \l_keys_path_tl { \keys_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:NNw
+ \exp_after:wN \l_@@_tmpa_tl
+ \exp_after:wN \l_@@_tmpb_tl
+ \l_keys_path_tl / \q_stop
+ \@@_cmd_set:nx { \l_keys_path_tl }
+ {
+ \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 , validate }
+ {
+ \cs_set_eq:cN
+ { \tl_use:c { c_@@_ ##1 _root_tl } \l_keys_path_tl }
+ \tex_undefined:D
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_value_requirement:nn}
+% \begin{macro}{\@@_validate_forbidden:, \@@_validate_required:}
+% \begin{macro}{\@@_validate_cleanup:w}
+% 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_@@_validate_root_tl \l_keys_path_tl }
+ { @@_validate_ #1 : }
+ }
+ { false }
+ {
+ \cs_if_eq:ccT
+ { \c_@@_validate_root_tl \l_keys_path_tl }
+ { @@_validate_ #1 : }
+ {
+ \cs_set_eq:cN
+ { \c_@@_validate_root_tl \l_keys_path_tl }
+ \tex_undefined:D
+ }
+ }
+ }
+ {
+ \__kernel_msg_error:nnx { kernel }
+ { key-property-boolean-values-only }
+ { .value_ #1 :n }
+ }
+ }
+\cs_new_protected:Npn \@@_validate_forbidden:
+ {
+ \bool_if:NF \l_@@_no_value_bool
+ {
+ \__kernel_msg_error:nnxx { kernel } { value-forbidden }
+ { \l_keys_path_tl } { \l_keys_value_tl }
+ \@@_validate_cleanup:w
+ }
+ }
+\cs_new_protected:Npn \@@_validate_required:
+ {
+ \bool_if:NT \l_@@_no_value_bool
+ {
+ \__kernel_msg_error:nnx { kernel } { value-required }
+ { \l_keys_path_tl }
+ \@@_validate_cleanup:w
+ }
+ }
+\cs_new_protected:Npn \@@_validate_cleanup:w #1 \cs_end: #2#3 { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_variable_set:NnnN, \@@_variable_set: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:nx { \l_keys_path_tl }
+ {
+ \exp_not:c { #2 _ #3 set:N #4 }
+ \exp_not:N #1
+ \exp_not:n { {##1} }
+ }
+ }
+\cs_generate_variant:Nn \@@_variable_set:NnnN { c }
+% \end{macrocode}
+% \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_tl .bool_set:N } #1
+ { \@@_bool_set:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .bool_set:c } #1
+ { \@@_bool_set:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .bool_gset:N } #1
+ { \@@_bool_set:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .bool_set_inverse:N } #1
+ { \@@_bool_set_inverse:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .bool_set_inverse:c } #1
+ { \@@_bool_set_inverse:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .bool_gset_inverse:N } #1
+ { \@@_bool_set_inverse:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .choice: }
+ { \@@_choice_make: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {.choices:nn, .choices:Vn, .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_tl .choices:nn } #1
+ { \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .choices:Vn } #1
+ { \exp_args:NV \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .choices:on } #1
+ { \exp_args:No \@@_choices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .code:n } #1
+ { \@@_cmd_set:nn { \l_keys_path_tl } {#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_tl .clist_set:N } #1
+ { \@@_variable_set:NnnN #1 { clist } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .clist_set:c } #1
+ { \@@_variable_set:cnnN {#1} { clist } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .clist_gset:N } #1
+ { \@@_variable_set:NnnN #1 { clist } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .clist_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { clist } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.default:n, .default:V, .default:o, .default:x}
+% Expansion is left to the internal functions.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_tl .default:n } #1
+ { \@@_default_set:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_tl .default:V } #1
+ { \exp_args:NV \@@_default_set:n #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .default:o } #1
+ { \exp_args:No \@@_default_set:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .dim_set:N } #1
+ { \@@_variable_set:NnnN #1 { dim } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .dim_set:c } #1
+ { \@@_variable_set:cnnN {#1} { dim } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .dim_gset:N } #1
+ { \@@_variable_set:NnnN #1 { dim } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .dim_gset:c } #1
+ { \@@_variable_set: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_tl .fp_set:N } #1
+ { \@@_variable_set:NnnN #1 { fp } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .fp_set:c } #1
+ { \@@_variable_set:cnnN {#1} { fp } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .fp_gset:N } #1
+ { \@@_variable_set:NnnN #1 { fp } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .fp_gset:c } #1
+ { \@@_variable_set: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_tl .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_tl .inherit:n } #1
+ { \@@_inherit:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.initial:n, .initial:V, .initial:o, .initial:x}
+% The standard hand-off approach.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_tl .initial:n } #1
+ { \@@_initialise:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_tl .initial:V } #1
+ { \exp_args:NV \@@_initialise:n #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .initial:o } #1
+ { \exp_args:No \@@_initialise:n {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .int_set:N } #1
+ { \@@_variable_set:NnnN #1 { int } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .int_set:c } #1
+ { \@@_variable_set:cnnN {#1} { int } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .int_gset:N } #1
+ { \@@_variable_set:NnnN #1 { int } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .int_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { int } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.meta:n}
+% Making a meta is handled internally.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .meta:nn } #1
+ { \@@_meta_make:nn #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{.multichoice:}
+% \begin{macro}
+% {
+% .multichoices:nn, .multichoices:Vn, .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_tl .multichoice: }
+ { \@@_multichoice_make: }
+\cs_new_protected:cpn { \c_@@_props_root_tl .multichoices:nn } #1
+ { \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .multichoices:Vn } #1
+ { \exp_args:NV \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .multichoices:on } #1
+ { \exp_args:No \@@_multichoices_make:nn #1 }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .muskip_set:N } #1
+ { \@@_variable_set:NnnN #1 { muskip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .muskip_set:c } #1
+ { \@@_variable_set:cnnN {#1} { muskip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .muskip_gset:N } #1
+ { \@@_variable_set:NnnN #1 { muskip } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .muskip_gset:c } #1
+ { \@@_variable_set: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_tl .prop_put:N } #1
+ { \@@_prop_put:Nn #1 { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .prop_put:c } #1
+ { \@@_prop_put:cn {#1} { } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .prop_gput:N } #1
+ { \@@_prop_put:Nn #1 { g } }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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_tl .skip_set:N } #1
+ { \@@_variable_set:NnnN #1 { skip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .skip_set:c } #1
+ { \@@_variable_set:cnnN {#1} { skip } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .skip_gset:N } #1
+ { \@@_variable_set:NnnN #1 { skip } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .skip_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { skip } { g } n }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{.tl_set:N, .tl_set:c}
+% \begin{macro}{.tl_gset:N, .tl_gset:c}
+% \begin{macro}{.tl_set_x:N, .tl_set_x:c}
+% \begin{macro}{.tl_gset_x:N, .tl_gset_x:c}
+% Setting a variable is very easy: just pass the data along.
+% \begin{macrocode}
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_set:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_set:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_set_x:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_set_x:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { } x }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { g } n }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset_x:N } #1
+ { \@@_variable_set:NnnN #1 { tl } { g } x }
+\cs_new_protected:cpn { \c_@@_props_root_tl .tl_gset_x:c } #1
+ { \@@_variable_set:cnnN {#1} { tl } { g } x }
+% \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_tl .undefine: }
+ { \@@_undefine: }
+% \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_tl .value_forbidden:n } #1
+ { \@@_value_requirement:nn { forbidden } {#1} }
+\cs_new_protected:cpn { \c_@@_props_root_tl .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:no,
+% \@@_set:nn
+% }
+% \begin{macro}{\@@_set:nnn}
+% A simple wrapper allowing for nesting.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_set:nn #1#2
+ {
+ \use:x
+ {
+ \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 , no }
+\cs_new_protected:Npn \@@_set:nn #1#2
+ { \exp_args:No \@@_set:nnn \l_@@_module_tl {#1} {#2} }
+\cs_new_protected:Npn \@@_set:nnn #1#2#3
+ {
+ \tl_set:Nx \l_@@_module_tl { \@@_trim_spaces:n {#2} }
+ \keyval_parse:NNn \@@_set_keyval:n \@@_set_keyval:nn {#3}
+ \tl_set:Nn \l_@@_module_tl {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \keys_set_known:nnN, \keys_set_known:nVN,
+% \keys_set_known:nvN, \keys_set_known:noN
+% }
+% \begin{macro}
+% {
+% \keys_set_known:nnnN, \keys_set_known:nVnN,
+% \keys_set_known:nvnN, \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:no
+% }
+% \begin{macro}{\@@_set_known:nnn}
+% Setting known keys simply means setting the appropriate flag, 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 , 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 , 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}
+ \tl_set:Nx #5 { \exp_not:o { \l_@@_unused_clist } }
+ \tl_set:Nn \l_@@_unused_clist {#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 , no }
+\cs_new_protected:Npn \@@_set_known:nnn #1#2#3
+ {
+ \use:x
+ {
+ \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_filter:nnnN, \keys_set_filter:nnVN, \keys_set_filter:nnvN,
+% \keys_set_filter:nnoN
+% }
+% \begin{macro}
+% {
+% \keys_set_filter:nnnnN, \keys_set_filter:nnVnN,
+% \keys_set_filter:nnvnN, \keys_set_filter:nnonN
+% }
+% \begin{macro}{\@@_set_filter:nnnnnN}
+% \begin{macro}
+% {
+% \keys_set_filter:nnn, \keys_set_filter:nnV, \keys_set_filter:nnv,
+% \keys_set_filter:nno
+% }
+% \begin{macro}{\@@_set_filter: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 flags
+% 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_filter:nnnN #1#2#3#4
+ {
+ \exp_args:No \@@_set_filter:nnnnnN
+ \l_@@_unused_clist
+ { \q_no_value } {#1} {#2} {#3} #4
+ }
+\cs_generate_variant:Nn \keys_set_filter:nnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \keys_set_filter:nnnnN #1#2#3#4#5
+ {
+ \exp_args:No \@@_set_filter:nnnnnN
+ \l_@@_unused_clist {#4} {#1} {#2} {#3} #5
+ }
+\cs_generate_variant:Nn \keys_set_filter:nnnnN { nnV , nnv , nno }
+\cs_new_protected:Npn \@@_set_filter:nnnnnN #1#2#3#4#5#6
+ {
+ \clist_clear:N \l_@@_unused_clist
+ \@@_set_filter:nnnn {#2} {#3} {#4} {#5}
+ \tl_set:Nx #6 { \exp_not:o { \l_@@_unused_clist } }
+ \tl_set:Nn \l_@@_unused_clist {#1}
+ }
+\cs_new_protected:Npn \keys_set_filter:nnn #1#2#3
+ {\@@_set_filter:nnnn { \q_no_value } {#1} {#2} {#3} }
+\cs_generate_variant:Nn \keys_set_filter:nnn { nnV , nnv , nno }
+\cs_new_protected:Npn \@@_set_filter:nnnn #1#2#3#4
+ {
+ \use:x
+ {
+ \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:x
+ {
+ \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
+ {
+ \seq_set_from_clist:Nn \l_@@_selective_seq {#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}{\@@_set_keyval:n, \@@_set_keyval:nn}
+% \begin{macro}{\@@_set_keyval:nnn, \@@_set_keyval:onn}
+% \begin{macro}{\@@_find_key_module:NNw}
+% \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 the 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_tl {#1} { }
+ }
+\cs_new_protected:Npn \@@_set_keyval:nn #1#2
+ {
+ \bool_set_false:N \l_@@_no_value_bool
+ \@@_set_keyval:onn \l_@@_module_tl {#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
+ {
+ \tl_set:Nx \l_keys_path_tl
+ {
+ \tl_if_blank:nF {#1}
+ { #1 / }
+ \@@_trim_spaces:n {#2}
+ }
+ \tl_clear:N \l_@@_module_tl
+ \tl_clear:N \l_@@_inherit_tl
+ \exp_after:wN \@@_find_key_module:NNw
+ \exp_after:wN \l_@@_module_tl
+ \exp_after:wN \l_keys_key_tl
+ \l_keys_path_tl / \q_stop
+ \@@_value_or_default:n {#3}
+ \bool_if:NTF \l_@@_selective_bool
+ { \@@_set_selective: }
+ { \@@_execute: }
+ \tl_set:Nn \l_@@_module_tl {#1}
+ }
+\cs_generate_variant:Nn \@@_set_keyval:nnn { o }
+\cs_new_protected:Npn \@@_find_key_module:NNw #1#2#3 / #4 \q_stop
+ {
+ \tl_if_blank:nTF {#4}
+ { \tl_set:Nn #2 {#3} }
+ {
+ \tl_put_right:Nx #1
+ {
+ \tl_if_empty:NF #1 { / }
+ #3
+ }
+ \@@_find_key_module:NNw #1#2 #4 \q_stop
+ }
+ }
+% \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_tl \l_keys_path_tl }
+ {
+ \clist_set_eq:Nc \l_@@_groups_clist
+ { \c_@@_groups_root_tl \l_keys_path_tl }
+ \@@_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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_check_groups:
+ {
+ \bool_set_false:N \l_@@_tmp_bool
+ \seq_map_inline:Nn \l_@@_selective_seq
+ {
+ \clist_map_inline:Nn \l_@@_groups_clist
+ {
+ \str_if_eq:nnT {##1} {####1}
+ {
+ \bool_set_true:N \l_@@_tmp_bool
+ \clist_map_break:n { \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}
+%
+% \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_tl \l_keys_path_tl }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_tl \l_keys_path_tl }
+ }
+ {
+ \tl_clear:N \l_keys_value_tl
+ \cs_if_exist:cT
+ { \c_@@_inherit_root_tl \@@_parent:o \l_keys_path_tl }
+ { \@@_default_inherit: }
+ }
+ }
+ { \tl_set:Nn \l_keys_value_tl {#1} }
+ }
+\cs_new_protected:Npn \@@_default_inherit:
+ {
+ \clist_map_inline:cn
+ { \c_@@_inherit_root_tl \@@_parent:o \l_keys_path_tl }
+ {
+ \cs_if_exist:cT
+ { \c_@@_default_root_tl ##1 / \l_keys_key_tl }
+ {
+ \tl_set_eq:Nc
+ \l_keys_value_tl
+ { \c_@@_default_root_tl ##1 / \l_keys_key_tl }
+ \clist_map_break:
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_execute:, \@@_execute_inherit:, \@@_execute_unknown:}
+% \begin{macro}[EXP]{\@@_execute:nn}
+% \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_tl \l_keys_path_tl }
+ {
+ \cs_if_exist_use:c { \c_@@_validate_root_tl \l_keys_path_tl }
+ \cs:w \c_@@_code_root_tl \l_keys_path_tl \exp_after:wN \cs_end:
+ \exp_after:wN { \l_keys_value_tl }
+ }
+ {
+ \cs_if_exist:cTF
+ { \c_@@_inherit_root_tl \@@_parent:o \l_keys_path_tl }
+ { \@@_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_tl \@@_parent:o \l_keys_path_tl }
+ {
+ \cs_if_exist:cT
+ { \c_@@_code_root_tl ##1 / \l_keys_key_tl }
+ {
+ \tl_set:Nn \l_@@_inherit_tl {##1}
+ \cs:w \c_@@_code_root_tl ##1 / \l_keys_key_tl
+ \exp_after:wN \cs_end: \exp_after:wN
+ { \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_tl \l_@@_module_tl / unknown }
+ {
+ \cs:w \c_@@_code_root_tl \l_@@_module_tl / unknown
+ \exp_after:wN \cs_end: \exp_after:wN { \l_keys_value_tl }
+ }
+ {
+ \__kernel_msg_error:nnxx { kernel } { key-unknown }
+ { \l_keys_path_tl } { \l_@@_module_tl }
+ }
+ }
+ }
+\cs_new:Npn \@@_execute:nn #1#2
+ {
+ \cs_if_exist:cTF { \c_@@_code_root_tl #1 }
+ {
+ \cs:w \c_@@_code_root_tl #1 \exp_after:wN \cs_end:
+ \exp_after:wN { \l_keys_value_tl }
+ }
+ {#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:Nx \l_@@_unused_clist
+ {
+ \exp_not:o \l_keys_key_tl
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ {
+ \tl_if_empty:NTF \l_@@_relative_tl
+ {
+ \clist_put_right:Nx \l_@@_unused_clist
+ {
+ \exp_not:o \l_keys_path_tl
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ { \@@_store_unused_aux: }
+ }
+ }
+\cs_new_protected:Npn \@@_store_unused_aux:
+ {
+ \tl_set:Nx \l_@@_relative_tl
+ { \exp_args:No \@@_trim_spaces:n \l_@@_relative_tl }
+ \use:x
+ {
+ \cs_set_protected:Npn \@@_store_unused:w
+ ####1 \l_@@_relative_tl /
+ ####2 \l_@@_relative_tl /
+ ####3 \exp_not:N \q_stop
+ }
+ {
+ \tl_if_blank:nF {##1}
+ {
+ \__kernel_msg_error:nnxx { kernel } { bad-relative-key-path }
+ \l_keys_path_tl
+ \l_@@_relative_tl
+ }
+ \clist_put_right:Nx \l_@@_unused_clist
+ {
+ \exp_not:n {##2}
+ \bool_if:NF \l_@@_no_value_bool
+ { = { \exp_not:o \l_keys_value_tl } }
+ }
+ }
+ \use:x
+ {
+ \@@_store_unused:w \l_keys_path_tl
+ \l_@@_relative_tl / \l_@@_relative_tl /
+ \exp_not:N \q_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
+ {
+ \tl_if_empty:NTF \l_@@_inherit_tl
+ { \@@_choice_find:nn { \l_keys_path_tl } {#1} }
+ {
+ \@@_choice_find:nn
+ { \l_@@_inherit_tl / \l_keys_key_tl } {#1}
+ }
+ }
+\cs_new:Npn \@@_choice_find:nn #1#2
+ {
+ \cs_if_exist:cTF { \c_@@_code_root_tl #1 / \@@_trim_spaces:n {#2} }
+ { \use:c { \c_@@_code_root_tl #1 / \@@_trim_spaces:n {#2} } {#2} }
+ { \use:c { \c_@@_code_root_tl #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:n, \@@_parent:o}
+% \begin{macro}[EXP]{\@@_parent:w}
+% Used to strip off the ending part of the key path after the last~|/|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parent:n #1
+ { \@@_parent:w #1 / / \q_stop { } }
+\cs_generate_variant:Nn \@@_parent:n { o }
+\cs_new:Npn \@@_parent:w #1 / #2 / #3 \q_stop #4
+ {
+ \tl_if_blank:nTF {#2}
+ {
+ \tl_if_blank:nF {#4}
+ { \use_none:n #4 }
+ }
+ {
+ \@@_parent:w #2 / #3 \q_stop { #4 / #1 }
+ }
+ }
+% \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.
+% \begin{macrocode}
+\cs_new:Npn \@@_trim_spaces:n #1
+ {
+ \exp_after:wN \@@_trim_spaces_auxi:w \tl_to_str:n {#1}
+ / \q_nil \q_stop
+
+ }
+\cs_new:Npn \@@_trim_spaces_auxi:w #1 / #2 \q_stop
+ {
+ \quark_if_nil:nTF {#2}
+ { \tl_trim_spaces:n {#1} }
+ { \@@_trim_spaces_auxii:w #1 / #2 }
+ }
+\cs_new:Npn \@@_trim_spaces_auxii:w #1 / #2 / \q_nil
+ {
+ \tl_trim_spaces:n {#1}
+ \@@_trim_spaces_auxiii:w #2 / \q_recursion_tail / \q_recursion_stop
+ }
+\cs_set:Npn \@@_trim_spaces_auxiii:w #1 /
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \tl_trim_spaces:n { / #1 }
+ \@@_trim_spaces_auxiii:w
+ }
+% \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_tl \@@_trim_spaces:n { #1 / #2 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \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_tl \@@_trim_spaces:n { #1 / #2 / #3 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\keys_show:nn, \keys_log:nn, \@@_show:Nnn}
+% To show a key, show its code using a message.
+% \begin{macrocode}
+\cs_new_protected:Npn \keys_show:nn
+ { \@@_show:Nnn \msg_show:nnxxxx }
+\cs_new_protected:Npn \keys_log:nn
+ { \@@_show:Nnn \msg_log:nnxxxx }
+\cs_new_protected:Npn \@@_show:Nnn #1#2#3
+ {
+ #1 { LaTeX / kernel } { show-key }
+ { \@@_trim_spaces:n { #2 / #3 } }
+ {
+ \keys_if_exist:nnT {#2} {#3}
+ {
+ \exp_args:Nnf \msg_show_item_unbraced:nn { code }
+ {
+ \exp_args:Nc \cs_replacement_spec:N
+ {
+ \c_@@_code_root_tl
+ \@@_trim_spaces:n { #2 / #3 }
+ }
+ }
+ }
+ }
+ { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% For when there is a need to complain.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { bad-relative-key-path }
+ { The~key~'#1'~is~not~inside~the~'#2'~path. }
+ { The~key~'#1'~cannot~be~expressed~relative~to~path~'#2'. }
+\__kernel_msg_new:nnnn { kernel } { boolean-values-only }
+ { Key~'#1'~accepts~boolean~values~only. }
+ { The~key~'#1'~only~accepts~the~values~'true'~and~'false'. }
+\__kernel_msg_new:nnnn { kernel } { key-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.
+ }
+\__kernel_msg_new:nnnn { kernel } { key-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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { value-required }
+ { The~key~'#1'~requires~a~value. }
+ {
+ The~key~'#1'~must~have~a~value.\\
+ No~value~was~present:~the~key~will~be~ignored.
+ }
+\__kernel_msg_new:nnn { kernel } { show-key }
+ {
+ The~key~#1~
+ \tl_if_empty:nTF {#2}
+ { is~undefined. }
+ { has~the~properties: #2 . }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3legacy.dtx b/macros/latex/contrib/l3kernel/l3legacy.dtx
new file mode 100644
index 0000000000..90ca8c5d0e
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3legacy.dtx
@@ -0,0 +1,100 @@
+% \iffalse meta-comment
+%
+%% File: l3legacy.dtx
+%
+% Copyright (C) 2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3legacy} package\\ Interfaces to legacy concepts^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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:nTF} \Arg{name} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \LaTeXe{}/plain \TeX{} conditional (generated by \tn{newif})
+% if \texttt{true} or \texttt{false} and branches accordingly. The
+% \meta{name} of the conditional should \emph{omit} the leading \texttt{if}.
+% \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.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \legacy_if:n #1 { p , T , F , TF }
+ {
+ \exp_args:Nc \if_meaning:w { if#1 } \iftrue
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3luatex.dtx b/macros/latex/contrib/l3kernel/l3luatex.dtx
new file mode 100644
index 0000000000..9557fc8a08
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3luatex.dtx
@@ -0,0 +1,539 @@
+% \iffalse meta-comment
+%
+%% File: l3luatex.dtx
+%
+% Copyright (C) 2010-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package: \LuaTeX-specific functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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}
+%
+% \section{Lua interfaces}
+%
+% As well as interfaces for \TeX{}, there are a small number of Lua functions
+% provided here.
+%
+% \begin{function}{l3kernel}
+% All public interfaces provided by the module are stored within the
+% |l3kernel| table.
+% \end{function}
+%
+% \begin{function}{l3kernel.charcat}
+% \begin{syntax}
+% |l3kernel.charcat(|\meta{charcode}, \meta{catcode}|)|
+% \end{syntax}
+% Constructs a character of \meta{charcode} and \meta{catcode} and returns
+% the result to \TeX{}.
+% \end{function}
+%
+% \begin{function}{l3kernel.elapsedtime}
+% \begin{syntax}
+% |l3kernel.elapsedtime()|
+% \end{syntax}
+% Returns the CPU time in \meta{scaled seconds} since the start of the
+% \TeX{} run or since |l3kernel.resettimer| was issued. This only
+% measures the time used by the CPU, not the real time, e.g., waiting
+% for user input.
+% \end{function}
+%
+% \begin{function}{l3kernel.filemdfivesum}
+% \begin{syntax}
+% |l3kernel.filemdfivesum(|\meta{file}|)|
+% \end{syntax}
+% Returns the of 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}{l3kernel.filemoddate}
+% \begin{syntax}
+% |l3kernel.filemoddate(|\meta{file}|)|
+% \end{syntax}
+% Returns the of 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}{l3kernel.filesize}
+% \begin{syntax}
+% |l3kernel.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}
+%
+% \begin{function}{l3kernel.resettimer}
+% \begin{syntax}
+% |l3kernel.resettimer()|
+% \end{syntax}
+% Resets the timer used by |l3kernel.elapsetime|.
+% \end{function}
+%
+% \begin{function}{l3kernel.strcmp}
+% \begin{syntax}
+% |l3kernel.strcmp(|\meta{str one}, \meta{str two}|)|
+% \end{syntax}
+% Compares the two strings and returns |0| to \TeX{}
+% if the two are identical.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3luatex} implementation}
+%
+% \begin{macrocode}
+%<*initex|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. 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}
+\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} } }
+\sys_if_engine_luatex:F
+ {
+ \clist_map_inline:nn
+ {
+ \lua_escape:n , \lua_escape:e ,
+ \lua_now:n , \lua_now:e
+ }
+ {
+ \cs_set:Npn #1 ##1
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { luatex-required } { #1 }
+ }
+ }
+ \clist_map_inline:nn
+ { \lua_shipout_e:n , \lua_shipout:n }
+ {
+ \cs_set_protected:Npn #1 ##1
+ {
+ \__kernel_msg_error:nnn
+ { kernel } { luatex-required } { #1 }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { luatex-required }
+ { LuaTeX~engine~not~in~use!~Ignoring~#1. }
+ {
+ The~feature~you~are~using~is~only~available~
+ with~the~LuaTeX~engine.~LaTeX3~ignored~'#1'.
+ }
+% \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}{l3kernel}
+% Create a table for the kernel's own use.
+% \begin{macrocode}
+l3kernel = l3kernel or { }
+% \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 unicode = unicode
+% \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 md5_sum = md5.sum
+local open = io.open
+local os_clock = os.clock
+local os_date = os.date
+local setcatcode = tex.setcatcode
+local sprint = tex.sprint
+local write = tex.write
+% \end{macrocode}
+%
+% Newer Con\TeX{}t releases replace the |unicode| library by |utf|.
+% \begin{macrocode}
+local utf8_char = (utf and utf.char) or unicode.utf8.char
+% \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)
+ write((gsub(str, ".",
+ function (ch) return format("%02X", byte(ch)) end)))
+end
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.charcat}
+% Creating arbitrary chars needs a category code table. As set up here,
+% one may have been assigned earlier (see \pkg{l3bootstrap}) or a hard-coded
+% one is used. The latter is intended for format mode and should be adjusted
+% to match an eventual allocator.
+% \begin{macrocode}
+local charcat_table = l3kernel.charcat_table or 1
+local function charcat(charcode, catcode)
+ setcatcode(charcat_table, charcode, catcode)
+ sprint(charcat_table, utf8_char(charcode))
+end
+l3kernel.charcat = charcat
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.elapsedtime, l3kernel.resettimer}
+% Simple timing set up: give the result from the system clock in scaled
+% seconds.
+% \begin{macrocode}
+local base_time = 0
+local function elapsedtime()
+ local val = (os_clock() - base_time) * 65536 + 0.5
+ if val > 2147483647 then
+ val = 2147483647
+ end
+ write(format("%d",floor(val)))
+end
+l3kernel.elapsedtime = elapsedtime
+local function resettimer()
+ base_time = os_clock()
+end
+l3kernel.resettimer = resettimer
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.filemdfivesum}
+% 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 filemdfivesum(name)
+ local file = kpse_find(name, "tex", true)
+ if file then
+ local f = open(file, "rb")
+ if f then
+ local data = f:read("*a")
+ escapehex(md5_sum(data))
+ f:close()
+ end
+ end
+end
+l3kernel.filemdfivesum = filemdfivesum
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.filemoddate}
+% See procedure \texttt{makepdftime} in \texttt{utils.c} of
+% \pdfTeX{}.
+% \begin{macrocode}
+local function filemoddate(name)
+ local file = kpse_find(name, "tex", true)
+ if file then
+ local date = lfs_attr(file, "modification")
+ if date then
+ local d = os_date("*t", date)
+ if d.sec >= 60 then
+ d.sec = 59
+ end
+ 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
+ local hours = floor(off / 60)
+ local mins = abs(off - hours * 60)
+ timezone = format("%+03d", hours)
+ .. "'" .. format("%02d", mins) .. "'"
+ end
+ write("D:"
+ .. format("%04d", d.year)
+ .. format("%02d", d.month)
+ .. format("%02d", d.day)
+ .. format("%02d", d.hour)
+ .. format("%02d", d.min)
+ .. format("%02d", d.sec)
+ .. timezone)
+ end
+ end
+end
+l3kernel.filemoddate = filemoddate
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.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
+ write(size)
+ end
+ end
+end
+l3kernel.filesize = filesize
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{l3kernel.strcmp}
+% String comparison which gives the same results as \pdfTeX{}'s
+% \tn{pdfstrcmp}, although the ordering should likely not be relied upon!
+% \begin{macrocode}
+local function strcmp(A, B)
+ if A == B then
+ write("0")
+ elseif A < B then
+ write("-1")
+ else
+ write("1")
+ end
+end
+l3kernel.strcmp = strcmp
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Generic \Lua{} and font support}
+%
+% \begin{macrocode}
+%<*initex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=alloc>
+% \end{macrocode}
+%
+% A small amount of generic code is used by almost all \LuaTeX{} material so
+% needs to be loaded by the format.
+% \begin{macrocode}
+attribute_count_name = "g_@@_attribute_int"
+bytecode_count_name = "g_@@_bytecode_int"
+chunkname_count_name = "g_@@_chunkname_int"
+whatsit_count_name = "g_@@_whatsit_int"
+require("ltluatex")
+% \end{macrocode}
+%
+% With the above available the font loader code used by plain \TeX{} and
+% \LaTeXe{} when used with \LuaTeX{} can be loaded here. This is thus being
+% treated more-or-less as part of the engine itself.
+% \begin{macrocode}
+require("luaotfload-main")
+local _void = luaotfload.main()
+% \end{macrocode}
+% \begin{macrocode}
+%</initex>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</lua>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3msg.dtx b/macros/latex/contrib/l3kernel/l3msg.dtx
new file mode 100644
index 0000000000..d005a8131f
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3msg.dtx
@@ -0,0 +1,2097 @@
+% \iffalse meta-comment
+%
+%% File: l3msg.dtx
+%
+% Copyright (C) 2009-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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} package\\ Messages^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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}.
+%
+% \begin{function}[updated = 2011-08-16]{\msg_new:nnnn, \msg_new:nnn}
+% \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, \msg_gset:nnnn, \msg_gset: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{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, 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{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} may be altered by use of
+% \cs{g_msg_module_documentation_prop}
+% \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. For example, \LaTeX{}3 core messages are stored in
+% the reserved |LaTeX| tree, but are printed as |LaTeX3|.
+% \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{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{x}-type variants should be used to expand material.
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_fatal:nnnnnn ,
+% \msg_fatal:nnnnn ,
+% \msg_fatal:nnnn ,
+% \msg_fatal:nnn ,
+% \msg_fatal:nn ,
+% \msg_fatal:nnxxxx ,
+% \msg_fatal:nnxxx ,
+% \msg_fatal:nnxx ,
+% \msg_fatal:nnx
+% }
+% \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:nnxxxx ,
+% \msg_critical:nnxxx ,
+% \msg_critical:nnxx ,
+% \msg_critical:nnx
+% }
+% \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:nnxxxx ,
+% \msg_error:nnxxx ,
+% \msg_error:nnxx ,
+% \msg_error:nnx
+% }
+% \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:nnxxxx ,
+% \msg_warning:nnxxx ,
+% \msg_warning:nnxx ,
+% \msg_warning:nnx
+% }
+% \begin{syntax}
+% \cs{msg_warning:nnxxxx} \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}[updated = 2012-08-11]
+% {
+% \msg_info:nnnnnn ,
+% \msg_info:nnnnn ,
+% \msg_info:nnnn ,
+% \msg_info:nnn ,
+% \msg_info:nn ,
+% \msg_info:nnxxxx ,
+% \msg_info:nnxxx ,
+% \msg_info:nnxx ,
+% \msg_info:nnx
+% }
+% \begin{syntax}
+% \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.
+% The information text is added to the log file.
+% \end{function}
+%
+% \begin{function}[updated = 2012-08-11]
+% {
+% \msg_log:nnnnnn ,
+% \msg_log:nnnnn ,
+% \msg_log:nnnn ,
+% \msg_log:nnn ,
+% \msg_log:nn ,
+% \msg_log:nnxxxx ,
+% \msg_log:nnxxx ,
+% \msg_log:nnxx ,
+% \msg_log:nnx
+% }
+% \begin{syntax}
+% \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 information text is added to the log file: the output
+% is briefer than \cs{msg_info:nnnnnn}.
+% \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:nnxxxx ,
+% \msg_none:nnxxx ,
+% \msg_none:nnxx ,
+% \msg_none:nnx
+% }
+% \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}
+%
+% \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}.
+% \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}
+%<*initex|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{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}
+\__kernel_patch:nnNNpn { }
+ {
+ \__kernel_debug_log:x
+ { Defining~message~ #1 / #2 ~\msg_line_context: }
+ }
+\cs_new_protected:Npn \@@_chk_free:nn #1#2
+ {
+ \msg_if_exist:nnT {#1} {#2}
+ {
+ \__kernel_msg_error:nnxx { kernel } { message-already-defined }
+ {#1} {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\msg_new:nnnn, \msg_new:nnn}
+% \begin{macro}{\msg_gset:nnnn, \msg_gset:nnn}
+% \begin{macro}{\msg_set:nnnn, \msg_set:nnn}
+% Setting a message simply means saving the appropriate text
+% into two functions. A sanity check first.
+% \begin{macrocode}
+\cs_new_protected:Npn \msg_new:nnnn #1#2
+ {
+ \@@_chk_free:nn {#1} {#2}
+ \msg_gset:nnnn {#1} {#2}
+ }
+\cs_new_protected:Npn \msg_new:nnn #1#2#3
+ { \msg_new:nnnn {#1} {#2} {#3} { } }
+\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} { } }
+\cs_new_protected:Npn \msg_gset:nnnn #1#2#3#4
+ {
+ \cs_gset:cpn { \c_@@_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#3}
+ \cs_gset:cpn { \c_@@_more_text_prefix_tl #1 / #2 }
+ ##1##2##3##4 {#4}
+ }
+\cs_new_protected:Npn \msg_gset:nnn #1#2#3
+ { \msg_gset:nnnn {#1} {#2} {#3} { } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages: support functions and text}
+%
+% \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:Nx \l_@@_text_str { #1 {#2} }
+ \str_set:Nx \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{x}-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:nxnN { \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:Nx \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.
+% This is already done by the \LaTeXe{} kernel, so to avoid messing up any
+% deliberate change by a user this is only set in format mode.
+% \begin{macrocode}
+%<*initex>
+\int_gset:Nn \tex_errorcontextlines:D { -1 }
+%</initex>
+% \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} }
+ \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_gput:Nnn \g_msg_module_name_prop { LaTeX } { LaTeX3 }
+\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} }
+%<*initex>
+ { Module }
+%</initex>
+%<*package>
+ { Package }
+%</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:x
+ {
+ \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:cpx { msg_ #1 :nnnnn } ##1##2##3##4##5
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpx { msg_ #1 :nnnn } ##1##2##3##4
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpx { msg_ #1 :nnn } ##1##2##3
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} { } { } { } }
+ \cs_new_protected:cpx { msg_ #1 :nn } ##1##2
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} { } { } { } { } }
+ \cs_new_protected:cpx { msg_ #1 :nnxxxx } ##1##2##3##4##5##6
+ {
+ \use:x
+ {
+ \exp_not:N \exp_not:n
+ { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} }
+ {##3} {##4} {##5} {##6}
+ }
+ }
+ \cs_new_protected:cpx { msg_ #1 :nnxxx } ##1##2##3##4##5
+ { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpx { msg_ #1 :nnxx } ##1##2##3##4
+ { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpx { msg_ #1 :nnx } ##1##2##3
+ { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} { } { } { } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_fatal:nnnnnn ,
+% \msg_fatal:nnnnn ,
+% \msg_fatal:nnnn ,
+% \msg_fatal:nnn ,
+% \msg_fatal:nn ,
+% \msg_fatal:nnxxxx ,
+% \msg_fatal:nnxxx ,
+% \msg_fatal:nnxx ,
+% \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:nnxxxx ,
+% \msg_critical:nnxxx ,
+% \msg_critical:nnxx ,
+% \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:nnxxxx ,
+% \msg_error:nnxxx ,
+% \msg_error:nnxx ,
+% \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.
+% \begin{macrocode}
+ \@@_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}
+% {
+% \msg_warning:nnnnnn ,
+% \msg_warning:nnnnn ,
+% \msg_warning:nnnn ,
+% \msg_warning:nnn ,
+% \msg_warning:nn ,
+% \msg_warning:nnxxxx ,
+% \msg_warning:nnxxx ,
+% \msg_warning:nnxx ,
+% \msg_warning:nnx
+% }
+% Warnings are printed to the terminal.
+% \begin{macrocode}
+ \@@_class_new:nn { warning }
+ {
+ \str_set:Nx \l_@@_text_str { \msg_warning_text:n {#1} }
+ \str_set:Nx \l_@@_name_str { \msg_module_name:n {#1} }
+ \iow_term:n { }
+ \iow_wrap:nxnN
+ {
+ \l_@@_text_str : ~
+ \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6}
+ }
+ {
+ ( \l_@@_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l_@@_text_str
+ - \str_count:N \l_@@_name_str
+ }
+ { ~ }
+ }
+ { } \iow_term:n
+ \iow_term:n { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_info:nnnnnn ,
+% \msg_info:nnnnn ,
+% \msg_info:nnnn ,
+% \msg_info:nnn ,
+% \msg_info:nn ,
+% \msg_info:nnxxxx ,
+% \msg_info:nnxxx ,
+% \msg_info:nnxx ,
+% \msg_info:nnx
+% }
+% Information only goes into the log.
+% \begin{macrocode}
+ \@@_class_new:nn { info }
+ {
+ \str_set:Nx \l_@@_text_str { \msg_info_text:n {#1} }
+ \str_set:Nx \l_@@_name_str { \msg_module_name:n {#1} }
+ \iow_log:n { }
+ \iow_wrap:nxnN
+ {
+ \l_@@_text_str : ~
+ \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6}
+ }
+ {
+ ( \l_@@_name_str )
+ \prg_replicate:nn
+ {
+ \str_count:N \l_@@_text_str
+ - \str_count:N \l_@@_name_str
+ }
+ { ~ }
+ }
+ { } \iow_log:n
+ \iow_log:n { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_log:nnnnnn ,
+% \msg_log:nnnnn ,
+% \msg_log:nnnn ,
+% \msg_log:nnn ,
+% \msg_log:nn ,
+% \msg_log:nnxxxx ,
+% \msg_log:nnxxx ,
+% \msg_log:nnxx ,
+% \msg_log:nnx
+% }
+% \enquote{Log} data is very similar to information, but with no extras
+% added.
+% \begin{macrocode}
+ \@@_class_new:nn { log }
+ {
+ \iow_wrap:nnnN
+ { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} }
+ { } { } \iow_log:n
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \msg_none:nnnnnn ,
+% \msg_none:nnnnn ,
+% \msg_none:nnnn ,
+% \msg_none:nnn ,
+% \msg_none:nn ,
+% \msg_none:nnxxxx ,
+% \msg_none:nnxxx ,
+% \msg_none:nnxx ,
+% \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:nnxxxx ,
+% \msg_show:nnxxx ,
+% \msg_show:nnxx ,
+% \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 that 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 \q_mark } { . \q_mark }
+ { \@@_show_dot:w } { \@@_show:w }
+ ^^J #1 \q_stop
+ }
+ { \@@_show:nn { ? #1 } { } }
+ }
+ \cs_new:Npn \@@_show_dot:w #1 ^^J > ~ #2 . \q_stop
+ { \@@_show:nn {#1} {#2} }
+ \cs_new:Npn \@@_show:w #1 ^^J > ~ #2 \q_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}{\@@_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 }
+ { \__kernel_msg_error:nnx { kernel } { message-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
+ {
+%<package> \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:Npx \@@_use_code:
+ {
+ \exp_not:n
+ {
+ \use:c { @@_ \l_@@_class_tl _code:nnnnnn }
+ {#2} {#3} {#4} {#5} {#6} {#7}
+ }
+ }
+ \@@_use_redirect_name:n { #2 / #3 }
+ }
+ }
+ { \__kernel_msg_error:nnxx { kernel } { message-unknown } {#2} {#3} }
+%<package> \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 \q_mark \@@_use_hierarchy:nwwN
+ / \q_mark \use_none_delimit_by_q_stop:w
+ \q_stop
+ \@@_use_redirect_module:n { }
+ }
+ }
+\cs_new_protected:Npn \@@_use_hierarchy:nwwN #1#2 / #3 \q_mark #4
+ {
+ \seq_put_left:Nn \l_@@_hierarchy_seq {#1}
+ #4 { #1 / #2 } #3 \q_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}
+ \__kernel_msg_warning:nnxxxx
+ { kernel } { message-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_new:nnnn, \__kernel_msg_new:nnn}
+% \begin{macro}{\__kernel_msg_set:nnnn, \__kernel_msg_set:nnn}
+% The kernel needs some messages of its own. These are created using
+% pre-built functions. Two functions are provided: one more general
+% and one which only has the short text part.
+% \begin{macrocode}
+\cs_new_protected:Npn \__kernel_msg_new:nnnn #1#2
+ { \msg_new:nnnn { LaTeX } { #1 / #2 } }
+\cs_new_protected:Npn \__kernel_msg_new:nnn #1#2
+ { \msg_new:nnn { LaTeX } { #1 / #2 } }
+\cs_new_protected:Npn \__kernel_msg_set:nnnn #1#2
+ { \msg_set:nnnn { LaTeX } { #1 / #2 } }
+\cs_new_protected:Npn \__kernel_msg_set:nnn #1#2
+ { \msg_set:nnn { LaTeX } { #1 / #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_kernel_class_new:nN}
+% \begin{macro}{\@@_kernel_class_new_aux:nN}
+% All the functions for kernel messages come in variants ranging
+% from~$0$ to~$4$ arguments. Those with less than~$4$ arguments are
+% defined in terms of the $4$-argument variant, in a way very similar
+% to \cs{@@_class_new:nn}. This auxiliary is destroyed at the end of
+% the group.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_kernel_class_new:nN #1
+ { \@@_kernel_class_new_aux:nN { __kernel_msg_ #1 } }
+ \cs_set_protected:Npn \@@_kernel_class_new_aux:nN #1#2
+ {
+ \cs_new_protected:cpn { #1 :nnnnnn } ##1##2##3##4##5##6
+ {
+ \use:x
+ {
+ \exp_not:n { #2 { LaTeX } { ##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:cpx { #1 :nnnnn } ##1##2##3##4##5
+ { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpx { #1 :nnnn } ##1##2##3##4
+ { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpx { #1 :nnn } ##1##2##3
+ { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} { } { } { } }
+ \cs_new_protected:cpx { #1 :nn } ##1##2
+ { \exp_not:c { #1 :nnnnnn } {##1} {##2} { } { } { } { } }
+ \cs_new_protected:cpx { #1 :nnxxxx } ##1##2##3##4##5##6
+ {
+ \use:x
+ {
+ \exp_not:N \exp_not:n
+ { \exp_not:c { #1 :nnnnnn } {##1} {##2} }
+ {##3} {##4} {##5} {##6}
+ }
+ }
+ \cs_new_protected:cpx { #1 :nnxxx } ##1##2##3##4##5
+ { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } }
+ \cs_new_protected:cpx { #1 :nnxx } ##1##2##3##4
+ { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } }
+ \cs_new_protected:cpx { #1 :nnx } ##1##2##3
+ { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} { } { } { } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_msg_fatal:nnnnnn ,
+% \__kernel_msg_fatal:nnnnn ,
+% \__kernel_msg_fatal:nnnn ,
+% \__kernel_msg_fatal:nnn ,
+% \__kernel_msg_fatal:nn ,
+% \__kernel_msg_fatal:nnxxxx ,
+% \__kernel_msg_fatal:nnxxx ,
+% \__kernel_msg_fatal:nnxx ,
+% \__kernel_msg_fatal:nnx ,
+% \__kernel_msg_error:nnnnnn ,
+% \__kernel_msg_error:nnnnn ,
+% \__kernel_msg_error:nnnn ,
+% \__kernel_msg_error:nnn ,
+% \__kernel_msg_error:nn ,
+% \__kernel_msg_error:nnxxxx ,
+% \__kernel_msg_error:nnxxx ,
+% \__kernel_msg_error:nnxx ,
+% \__kernel_msg_error:nnx
+% }
+% Neither fatal kernel errors nor kernel errors can be redirected. We
+% directly use the code for (non-kernel) fatal errors and errors,
+% adding the \enquote{\LaTeX{}} module name. Three functions are
+% already defined by \pkg{l3basics}; we need to undefine them to avoid
+% errors.
+% \begin{macrocode}
+ \@@_kernel_class_new:nN { fatal } \@@_fatal_code:nnnnnn
+ \cs_undefine:N \__kernel_msg_error:nnxx
+ \cs_undefine:N \__kernel_msg_error:nnx
+ \cs_undefine:N \__kernel_msg_error:nn
+ \@@_kernel_class_new:nN { error } \@@_error_code:nnnnnn
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_msg_warning:nnnnnn ,
+% \__kernel_msg_warning:nnnnn ,
+% \__kernel_msg_warning:nnnn ,
+% \__kernel_msg_warning:nnn ,
+% \__kernel_msg_warning:nn ,
+% \__kernel_msg_warning:nnxxxx ,
+% \__kernel_msg_warning:nnxxx ,
+% \__kernel_msg_warning:nnxx ,
+% \__kernel_msg_warning:nnx ,
+% \__kernel_msg_info:nnnnnn ,
+% \__kernel_msg_info:nnnnn ,
+% \__kernel_msg_info:nnnn ,
+% \__kernel_msg_info:nnn ,
+% \__kernel_msg_info:nn ,
+% \__kernel_msg_info:nnxxxx ,
+% \__kernel_msg_info:nnxxx ,
+% \__kernel_msg_info:nnxx ,
+% \__kernel_msg_info:nnx
+% }
+% Kernel messages which can be redirected simply use the machinery for
+% normal messages, with the module name \enquote{\LaTeX{}}.
+% \begin{macrocode}
+ \@@_kernel_class_new:nN { warning } \msg_warning:nnxxxx
+ \@@_kernel_class_new:nN { info } \msg_info:nnxxxx
+% \end{macrocode}
+% \end{macro}
+%
+% End the group to eliminate \cs{@@_kernel_class_new:nN}.
+% \begin{macrocode}
+\group_end:
+% \end{macrocode}
+%
+% Error messages needed to actually implement the message system
+% itself.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { message-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
+ }
+\__kernel_msg_new:nnnn { kernel } { message-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
+ }
+\__kernel_msg_new:nnnn { kernel } { message-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
+ }
+\__kernel_msg_new:nnnn { kernel } { message-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}
+\__kernel_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.
+ }
+\__kernel_msg_new:nnn { kernel } { char-active }
+ { Cannot~generate~active~chars. }
+\__kernel_msg_new:nnn { kernel } { char-invalid-catcode }
+ { Invalid~catcode~for~char~generation. }
+\__kernel_msg_new:nnn { kernel } { char-null-space }
+ { Cannot~generate~null~char~as~a~space. }
+\__kernel_msg_new:nnn { kernel } { char-out-of-range }
+ { Charcode~requested~out~of~engine~range. }
+\__kernel_msg_new:nnn { kernel } { char-space }
+ { Cannot~generate~space~chars. }
+\__kernel_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
+ }
+\__kernel_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.
+ }
+\__kernel_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!
+ }
+\__kernel_msg_new:nnnn { kernel } { out-of-registers }
+ { No~room~for~a~new~#1. }
+ {
+ TeX~only~supports~\int_use:N \c_max_register_int \ %
+ of~each~type.~All~the~#1~registers~have~been~used.~
+ This~run~will~be~aborted~now.
+ }
+\__kernel_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'.~
+ To~define~variants~use~\iow_char:N\\cs_generate_variant:Nn~
+ and~to~define~other~functions~use~\iow_char:N\\cs_new:Npn.
+ }
+\__kernel_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.
+ }
+\__kernel_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.
+ }
+\__kernel_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.
+ }
+\__kernel_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.
+ }
+\__kernel_msg_new:nnn { kernel } { randint-backward-range }
+ { Bounds~ordered~backwards~in~\iow_char:N\\int_rand:nn~{#1}~{#2}. }
+\__kernel_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.
+ }
+\__kernel_msg_new:nnnn { kernel } { key-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.
+ }
+\__kernel_msg_new:nnnn { kernel } { key-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'.
+ }
+\__kernel_msg_new:nnnn { kernel } { key-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.
+ }
+\__kernel_msg_new:nnnn { kernel } { key-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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_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.
+ }
+\__kernel_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'.
+ }
+\__kernel_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'.
+ }
+\__kernel_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.
+ }
+\__kernel_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. }
+ } { . }
+ }
+% \end{macrocode}
+%
+% Some errors are only needed in package mode if debugging is enabled by
+% one of the options \texttt{enable-debug}, \texttt{check-declarations},
+% \texttt{log-functions}, or on the contrary if debugging is turned off.
+% In format mode the error is somewhat different.
+% \begin{macrocode}
+%<*package>
+\__kernel_if_debug:TF
+ {
+ \__kernel_msg_new:nnnn { kernel } { 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~
+ 'check-declarations',~'deprecation',~'log-functions',~not~'#1'.
+ }
+ \__kernel_msg_new:nnn { kernel } { expr } { '#2'~in~#1 }
+ \__kernel_msg_new:nnnn { kernel } { 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'.
+ }
+ \__kernel_msg_new:nnnn { kernel } { 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~create~the~variable~and~continue.
+ }
+ }
+ {
+ \__kernel_msg_new:nnnn { kernel } { enable-debug }
+ { To~use~'#1'~load~expl3~with~the~'enable-debug'~option. }
+ {
+ The~function~'#1'~will~be~ignored~because~it~can~only~work~if~
+ some~internal~functions~in~expl3~have~been~appropriately~
+ defined.~This~only~happens~if~one~of~the~options~
+ 'enable-debug',~'check-declarations'~or~'log-functions'~was~
+ given~when~loading~expl3.
+ }
+ }
+%</package>
+%<*initex>
+\__kernel_msg_new:nnnn { kernel } { enable-debug }
+ { '#1'~cannot~be~used~in~format~mode. }
+ {
+ The~function~'#1'~will~be~ignored~because~it~can~only~work~if~
+ some~internal~functions~in~expl3~have~been~appropriately~
+ defined.~This~only~happens~in~package~mode~(and~only~if~one~of~
+ the~options~'enable-debug',~'check-declarations'~or~'log-functions'~
+ was~given~when~loading~expl3.
+ }
+%</initex>
+% \end{macrocode}
+%
+% Some errors only appear in expandable settings,
+% hence don't need a \enquote{more-text} argument.
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { bad-exp-end-f }
+ { Misused~\exp_end_continue_f:w or~:nw }
+\__kernel_msg_new:nnn { kernel } { bad-variable }
+ { Erroneous~variable~#1 used! }
+\__kernel_msg_new:nnn { kernel } { misused-sequence }
+ { A~sequence~was~misused. }
+\__kernel_msg_new:nnn { kernel } { misused-prop }
+ { A~property~list~was~misused. }
+\__kernel_msg_new:nnn { kernel } { negative-replication }
+ { Negative~argument~for~\iow_char:N\\prg_replicate:nn. }
+\__kernel_msg_new:nnn { kernel } { prop-keyval }
+ { Missing/extra~'='~in~'#1'~(in~'..._keyval:Nn') }
+\__kernel_msg_new:nnn { kernel } { unknown-comparison }
+ { Relation~'#1'~unknown:~use~=,~<,~>,~==,~!=,~<=,~>=. }
+\__kernel_msg_new:nnn { kernel } { zero-step }
+ { Zero~step~size~for~step~function~#1. }
+\cs_if_exist:NF \tex_expanded:D
+ {
+ \__kernel_msg_new:nnn { kernel } { e-type }
+ { #1 ~ in~e-type~argument }
+ }
+% \end{macrocode}
+%
+% Messages used by the \enquote{\texttt{show}} functions.
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { show-clist }
+ {
+ The~comma~list~ \tl_if_empty:nF {#1} { #1 ~ }
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\__kernel_msg_new:nnn { kernel } { show-intarray }
+ { The~integer~array~#1~contains~#2~items: \\ #3 . }
+\__kernel_msg_new:nnn { kernel } { show-prop }
+ {
+ The~property~list~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~pairs~(without~outer~braces): #2 . }
+ }
+\__kernel_msg_new:nnn { kernel } { show-seq }
+ {
+ The~sequence~#1~
+ \tl_if_empty:nTF {#2}
+ { is~empty \\>~ . }
+ { contains~the~items~(without~outer~braces): #2 . }
+ }
+\__kernel_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}
+%
+% \subsection{Expandable errors}
+%
+% \begin{macro}{\@@_expandable_error:n}
+% \begin{macro}{\@@_expandable_error:w}
+% In expansion only context, we cannot use the normal means of
+% reporting errors. Instead, we feed \TeX{} an undefined control
+% sequence, \cs{LaTeX3 error:}. It is thus interrupted, and shows
+% the context, which thanks to the odd-looking \cs{use:n} is
+% \begin{verbatim}
+% <argument> \LaTeX3 error:
+% The error message.
+% \end{verbatim}
+% In other words, \TeX{} is processing the argument of \cs{use:n},
+% which is \cs{LaTeX3 error:} \meta{error message}.
+% Then \cs{@@_expandable_error:w} cleans up. In fact, there
+% is an extra subtlety: if the user inserts tokens for error recovery,
+% they should be kept. Thus we also use an odd space character
+% (with category code $7$) and keep tokens until that space character,
+% dropping everything else until \cs{q_stop}. The \cs{exp_end:} prevents
+% losing braces around the user-inserted text if any, and stops the
+% expansion of \cs{exp:w}. The group is used to prevent
+% |\LaTeX3~error:| from being globally equal to \cs{scan_stop:}.
+% \begin{macrocode}
+\group_begin:
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new:Npn \@@_expandable_error:n ##1
+ {
+ \exp:w
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_expandable_error:w
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \exp_end:
+ \use:n { #1 #2 ##1 } #2
+ }
+ \cs_new:Npn \@@_expandable_error:w ##1 #2 ##2 #2 {##1}
+ }
+\exp_args:Ncx \@@_tmp:w { LaTeX3~error: }
+ { \char_generate:nn { `\ } { 7 } }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \__kernel_msg_expandable_error:nnnnnn,
+% \__kernel_msg_expandable_error:nnnnn,
+% \__kernel_msg_expandable_error:nnnn,
+% \__kernel_msg_expandable_error:nnn,
+% \__kernel_msg_expandable_error:nn,
+% \__kernel_msg_expandable_error:nnffff,
+% \__kernel_msg_expandable_error:nnfff,
+% \__kernel_msg_expandable_error:nnff,
+% \__kernel_msg_expandable_error:nnf,
+% }
+% The command built from the csname
+% \cs{c_@@_text_prefix_tl} |LaTeX / #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.
+% \begin{macrocode}
+\exp_args_generate:n { oooo }
+\cs_new:Npn \__kernel_msg_expandable_error:nnnnnn #1#2#3#4#5#6
+ {
+ \exp_args:Ne \@@_expandable_error:n
+ {
+ \exp_args:Nc \exp_args:Noooo
+ { \c_@@_text_prefix_tl LaTeX / #1 / #2 }
+ { \tl_to_str:n {#3} }
+ { \tl_to_str:n {#4} }
+ { \tl_to_str:n {#5} }
+ { \tl_to_str:n {#6} }
+ }
+ }
+\cs_new:Npn \__kernel_msg_expandable_error:nnnnn #1#2#3#4#5
+ {
+ \__kernel_msg_expandable_error:nnnnnn
+ {#1} {#2} {#3} {#4} {#5} { }
+ }
+\cs_new:Npn \__kernel_msg_expandable_error:nnnn #1#2#3#4
+ {
+ \__kernel_msg_expandable_error:nnnnnn
+ {#1} {#2} {#3} {#4} { } { }
+ }
+\cs_new:Npn \__kernel_msg_expandable_error:nnn #1#2#3
+ {
+ \__kernel_msg_expandable_error:nnnnnn
+ {#1} {#2} {#3} { } { } { }
+ }
+\cs_new:Npn \__kernel_msg_expandable_error:nn #1#2
+ {
+ \__kernel_msg_expandable_error:nnnnnn
+ {#1} {#2} { } { } { } { }
+ }
+\cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnnnn { nnffff }
+\cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnnn { nnfff }
+\cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnn { nnff }
+\cs_generate_variant:Nn \__kernel_msg_expandable_error:nnn { nnf }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3names.dtx b/macros/latex/contrib/l3kernel/l3names.dtx
new file mode 100644
index 0000000000..9f0bc82400
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3names.dtx
@@ -0,0 +1,2064 @@
+% \iffalse meta-comment
+%
+%% File: l3names.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Namespace for primitives^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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 switches to the category code r{\'e}gime for programming;
+% \item provides support settings for building the code as a \TeX{} format.
+% \end{itemize}
+%
+% This module is entirely dedicated to primitives, 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}
+%<*initex|package>
+% \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. In format mode, it also deletes all of the existing names (although
+% some do come back later).
+%
+% 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. This also allows the
+% original names to be removed in format mode.
+% \begin{macrocode}
+ \long \def \@@_primitive:NN #1#2
+ {
+ \tex_global:D \tex_let:D #2 #1
+%<*initex>
+ \tex_global:D \tex_let:D #1 \tex_undefined:D
+%</initex>
+ }
+% \end{macrocode}
+%\end{macro}
+%
+% To allow extracting \enquote{just the names}, a bit of DocStrip fiddling.
+% \begin{macrocode}
+%</initex|package>
+%<*initex|names|package>
+% \end{macrocode}
+%
+% In the current incarnation of this package, 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 \pdfcreationdate \tex_pdfcreationdate: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 \pdffontattr \tex_pdffontattr:D
+ \@@_primitive:NN \pdffontname \tex_pdffontname:D
+ \@@_primitive:NN \pdffontobjnum \tex_pdffontobjnum:D
+ \@@_primitive:NN \pdfgamma \tex_pdfgamma:D
+ \@@_primitive:NN \pdfimageapplygamma \tex_pdfimageapplygamma:D
+ \@@_primitive:NN \pdfimagegamma \tex_pdfimagegamma:D
+ \@@_primitive:NN \pdfgentounicode \tex_pdfgentounicode:D
+ \@@_primitive:NN \pdfglyphtounicode \tex_pdfglyphtounicode:D
+ \@@_primitive:NN \pdfhorigin \tex_pdfhorigin: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 \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 \pdfminorversion \tex_pdfminorversion:D
+ \@@_primitive:NN \pdfnames \tex_pdfnames:D
+ \@@_primitive:NN \pdfobj \tex_pdfobj:D
+ \@@_primitive:NN \pdfobjcompresslevel \tex_pdfobjcompresslevel: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 \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 \pdfthread \tex_pdfthread:D
+ \@@_primitive:NN \pdfthreadmargin \tex_pdfthreadmargin:D
+ \@@_primitive:NN \pdftrailer \tex_pdftrailer:D
+ \@@_primitive:NN \pdfuniqueresname \tex_pdfuniqueresname:D
+ \@@_primitive:NN \pdfvorigin \tex_pdfvorigin:D
+ \@@_primitive:NN \pdfxform \tex_pdfxform:D
+ \@@_primitive:NN \pdfxformattr \tex_pdfxformattr:D
+ \@@_primitive:NN \pdfxformname \tex_pdfxformname:D
+ \@@_primitive:NN \pdfxformresources \tex_pdfxformresources: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 \pdfadjustspacing \tex_adjustspacing:D
+ \@@_primitive:NN \pdfcopyfont \tex_copyfont: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 \pdffiledump \tex_filedump:D
+ \@@_primitive:NN \pdffilemoddate \tex_filemoddate:D
+ \@@_primitive:NN \pdffilesize \tex_filesize: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 \pdflastxpos \tex_lastxpos:D
+ \@@_primitive:NN \pdflastypos \tex_lastypos:D
+ \@@_primitive:NN \pdfmapfile \tex_mapfile:D
+ \@@_primitive:NN \pdfmapline \tex_mapline:D
+ \@@_primitive:NN \pdfmdfivesum \tex_mdfivesum: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 \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 \pdfstrcmp \tex_strcmp:D
+ \@@_primitive:NN \pdfsetrandomseed \tex_setrandomseed:D
+ \@@_primitive:NN \pdfshellescape \tex_shellescape:D
+ \@@_primitive:NN \pdftracingfonts \tex_tracingfonts: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 \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 \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}
+%</initex|names|package>
+%<*initex|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 }
+%<*initex>
+ \tex_global:D \tex_let:D #1 \tex_undefined:D
+%</initex>
+ }
+%</initex|package>
+%<*initex|names|package>
+% \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
+% \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
+% \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 \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 \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 \directlua \tex_directlua:D
+ \@@_primitive:NN \dviextension \tex_dviextension:D
+ \@@_primitive:NN \dvifeedback \tex_dvifeedback:D
+ \@@_primitive:NN \dvivariable \tex_dvivariable:D
+ \@@_primitive:NN \etoksapp \tex_etoksapp:D
+ \@@_primitive:NN \etokspre \tex_etokspre:D
+ \@@_primitive:NN \exceptionpenalty \tex_exceptionpenalty: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 \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 \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 \mathdelimitersmode \tex_mathdelimitersmode:D
+ \@@_primitive:NN \mathdir \tex_mathdir:D
+ \@@_primitive:NN \mathdirection \tex_mathdirection:D
+ \@@_primitive:NN \mathdisplayskipmode \tex_mathdisplayskipmode:D
+ \@@_primitive:NN \matheqnogapstep \tex_matheqnogapstep: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 \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 \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 \vpack \tex_vpack: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_pagewith: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 \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 \Unosubscript \tex_Unosubscript:D
+ \@@_primitive:NN \Unosuperscript \tex_Unosuperscript:D
+ \@@_primitive:NN \Uoverdelimiter \tex_Uoverdelimiter:D
+ \@@_primitive:NN \Uradical \tex_Uradical: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 Harf\TeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \harftexrevision \tex_harftexrevision:D
+ \@@_primitive:NN \harftexversion \tex_harftexversion:D
+% \end{macrocode}
+% Primitives from \pTeX{}.
+% \begin{macrocode}
+ \@@_primitive:NN \autospacing \tex_autospacing:D
+ \@@_primitive:NN \autoxspacing \tex_autoxspacing: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 \ifdbox \tex_ifdbox:D
+ \@@_primitive:NN \ifddir \tex_ifddir:D
+ \@@_primitive:NN \ifmdir \tex_ifmdir:D
+ \@@_primitive:NN \iftbox \tex_iftbox: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 \noautospacing \tex_noautospacing:D
+ \@@_primitive:NN \noautoxspacing \tex_noautoxspacing:D
+ \@@_primitive:NN \postbreakpenalty \tex_postbreakpenalty:D
+ \@@_primitive:NN \prebreakpenalty \tex_prebreakpenalty:D
+ \@@_primitive:NN \ptexminorversion \tex_ptexminorversion:D
+ \@@_primitive:NN \ptexrevision \tex_ptexrevision:D
+ \@@_primitive:NN \ptexversion \tex_ptexversion: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 \tfont \tex_tfont: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
+% \end{macrocode}
+% Primitives from \upTeX{}.
+% \begin{macrocode}
+ \@@_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 \ucs \tex_ucs:D
+ \@@_primitive:NN \uptexrevision \tex_uptexrevision:D
+ \@@_primitive:NN \uptexversion \tex_uptexversion:D
+% \end{macrocode}
+% End of the \enquote{just the names} part of the source.
+% \begin{macrocode}
+%</initex|names|package>
+%<*initex|package>
+% \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.
+% A convenient test for \LaTeXe{} is the \cs{@@@@end} saved primitive.
+% \begin{macrocode}
+%<*package>
+\tex_ifdefined:D \@@@@end
+ \tex_let:D \tex_end:D \@@@@end
+ \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_input:D \@@@@input
+ \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 \luatextracingfonts
+ \tex_fi:D
+ \tex_fi:D
+\tex_fi:D
+% \end{macrocode}
+% That is also true for the \LuaTeX{} primitives under \LaTeXe{}
+% (depending on the format-building date). There are a few primitives
+% that get the right names anyway so are missing here!
+% \begin{macrocode}
+\tex_ifdefined:D \luatexsuppressfontnotfounderror
+ \tex_let:D \tex_alignmark:D \luatexalignmark
+ \tex_let:D \tex_aligntab:D \luatexaligntab
+ \tex_let:D \tex_attribute:D \luatexattribute
+ \tex_let:D \tex_attributedef:D \luatexattributedef
+ \tex_let:D \tex_catcodetable:D \luatexcatcodetable
+ \tex_let:D \tex_clearmarks:D \luatexclearmarks
+ \tex_let:D \tex_crampeddisplaystyle:D \luatexcrampeddisplaystyle
+ \tex_let:D \tex_crampedscriptscriptstyle:D
+ \luatexcrampedscriptscriptstyle
+ \tex_let:D \tex_crampedscriptstyle:D \luatexcrampedscriptstyle
+ \tex_let:D \tex_crampedtextstyle:D \luatexcrampedtextstyle
+ \tex_let:D \tex_fontid:D \luatexfontid
+ \tex_let:D \tex_formatname:D \luatexformatname
+ \tex_let:D \tex_gleaders:D \luatexgleaders
+ \tex_let:D \tex_initcatcodetable:D \luatexinitcatcodetable
+ \tex_let:D \tex_latelua:D \luatexlatelua
+ \tex_let:D \tex_luaescapestring:D \luatexluaescapestring
+ \tex_let:D \tex_luafunction:D \luatexluafunction
+ \tex_let:D \tex_mathstyle:D \luatexmathstyle
+ \tex_let:D \tex_nokerns:D \luatexnokerns
+ \tex_let:D \tex_noligs:D \luatexnoligs
+ \tex_let:D \tex_outputbox:D \luatexoutputbox
+ \tex_let:D \tex_pageleftoffset:D \luatexpageleftoffset
+ \tex_let:D \tex_pagetopoffset:D \luatexpagetopoffset
+ \tex_let:D \tex_postexhyphenchar:D \luatexpostexhyphenchar
+ \tex_let:D \tex_posthyphenchar:D \luatexposthyphenchar
+ \tex_let:D \tex_preexhyphenchar:D \luatexpreexhyphenchar
+ \tex_let:D \tex_prehyphenchar:D \luatexprehyphenchar
+ \tex_let:D \tex_savecatcodetable:D \luatexsavecatcodetable
+ \tex_let:D \tex_scantextokens:D \luatexscantextokens
+ \tex_let:D \tex_suppressifcsnameerror:D
+ \luatexsuppressifcsnameerror
+ \tex_let:D \tex_suppresslongerror:D \luatexsuppresslongerror
+ \tex_let:D \tex_suppressmathparerror:D
+ \luatexsuppressmathparerror
+ \tex_let:D \tex_suppressoutererror:D \luatexsuppressoutererror
+ \tex_let:D \tex_Uchar:D \luatexUchar
+ \tex_let:D \tex_suppressfontnotfounderror:D
+ \luatexsuppressfontnotfounderror
+% \end{macrocode}
+% Which also covers those slightly odd ones.
+% \begin{macrocode}
+ \tex_let:D \tex_bodydir:D \luatexbodydir
+ \tex_let:D \tex_boxdir:D \luatexboxdir
+ \tex_let:D \tex_leftghost:D \luatexleftghost
+ \tex_let:D \tex_localbrokenpenalty:D \luatexlocalbrokenpenalty
+ \tex_let:D \tex_localinterlinepenalty:D
+ \luatexlocalinterlinepenalty
+ \tex_let:D \tex_localleftbox:D \luatexlocalleftbox
+ \tex_let:D \tex_localrightbox:D \luatexlocalrightbox
+ \tex_let:D \tex_mathdir:D \luatexmathdir
+ \tex_let:D \tex_pagebottomoffset:D \luatexpagebottomoffset
+ \tex_let:D \tex_pagedir:D \luatexpagedir
+ \tex_let:D \tex_pageheight:D \luatexpageheight
+ \tex_let:D \tex_pagerightoffset:D \luatexpagerightoffset
+ \tex_let:D \tex_pagewidth:D \luatexpagewidth
+ \tex_let:D \tex_pardir:D \luatexpardir
+ \tex_let:D \tex_rightghost:D \luatexrightghost
+ \tex_let:D \tex_textdir:D \luatextextdir
+\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_mapfile:D \tex_undefined:D
+ \tex_let:D \tex_mapline:D \tex_undefined:D
+\tex_fi:D
+%</package>
+% \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}
+% Up to v0.80, \LuaTeX{} defines the \pdfTeX{} version data: rather
+% confusing. Removing them means that \cs{tex_pdftexversion:D} is
+% a marker for \pdfTeX{} alone: useful in engine-dependent code later.
+% \begin{macrocode}
+%<*initex|package>
+\tex_ifdefined:D \tex_luatexversion:D
+ \tex_let:D \tex_pdftexbanner:D \tex_undefined:D
+ \tex_let:D \tex_pdftexrevision:D \tex_undefined:D
+ \tex_let:D \tex_pdftexversion:D \tex_undefined:D
+\tex_fi:D
+%</initex|package>
+% \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}
+%<*package>
+\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
+%</package>
+% \end{macrocode}
+%
+% \subsection{Deprecated functions}
+%
+% Older versions of \pkg{expl3} divided up primitives by \enquote{source}: that
+% becomes very tricky with multiple parallel engine developments, so has been
+% dropped. To cover the transition, we provide the older names here for a
+% limited period (until the end of 2019).
+%
+% To allow \cs{debug_on:n} \Arg{deprecation} to work we save the list of
+% primitives into \cs{@@_primitives:}
+% \begin{macrocode}
+%<*package>
+\tex_begingroup: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 \@@_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 }
+ }
+ \tex_xdef:D \@@_primitives:
+ {
+ \tex_unexpanded:D
+ {
+ \@@_primitive:NN \beginL \etex_beginL:D
+ \@@_primitive:NN \beginR \etex_beginR:D
+ \@@_primitive:NN \botmarks \etex_botmarks:D
+ \@@_primitive:NN \clubpenalties \etex_clubpenalties:D
+ \@@_primitive:NN \currentgrouplevel \etex_currentgrouplevel:D
+ \@@_primitive:NN \currentgrouptype \etex_currentgrouptype:D
+ \@@_primitive:NN \currentifbranch \etex_currentifbranch:D
+ \@@_primitive:NN \currentiflevel \etex_currentiflevel:D
+ \@@_primitive:NN \currentiftype \etex_currentiftype:D
+ \@@_primitive:NN \detokenize \etex_detokenize:D
+ \@@_primitive:NN \dimexpr \etex_dimexpr:D
+ \@@_primitive:NN \displaywidowpenalties
+ \etex_displaywidowpenalties:D
+ \@@_primitive:NN \endL \etex_endL:D
+ \@@_primitive:NN \endR \etex_endR:D
+ \@@_primitive:NN \eTeXrevision \etex_eTeXrevision:D
+ \@@_primitive:NN \eTeXversion \etex_eTeXversion:D
+ \@@_primitive:NN \everyeof \etex_everyeof:D
+ \@@_primitive:NN \firstmarks \etex_firstmarks:D
+ \@@_primitive:NN \fontchardp \etex_fontchardp:D
+ \@@_primitive:NN \fontcharht \etex_fontcharht:D
+ \@@_primitive:NN \fontcharic \etex_fontcharic:D
+ \@@_primitive:NN \fontcharwd \etex_fontcharwd:D
+ \@@_primitive:NN \glueexpr \etex_glueexpr:D
+ \@@_primitive:NN \glueshrink \etex_glueshrink:D
+ \@@_primitive:NN \glueshrinkorder \etex_glueshrinkorder:D
+ \@@_primitive:NN \gluestretch \etex_gluestretch:D
+ \@@_primitive:NN \gluestretchorder \etex_gluestretchorder:D
+ \@@_primitive:NN \gluetomu \etex_gluetomu:D
+ \@@_primitive:NN \ifcsname \etex_ifcsname:D
+ \@@_primitive:NN \ifdefined \etex_ifdefined:D
+ \@@_primitive:NN \iffontchar \etex_iffontchar:D
+ \@@_primitive:NN \interactionmode \etex_interactionmode:D
+ \@@_primitive:NN \interlinepenalties \etex_interlinepenalties:D
+ \@@_primitive:NN \lastlinefit \etex_lastlinefit:D
+ \@@_primitive:NN \lastnodetype \etex_lastnodetype:D
+ \@@_primitive:NN \marks \etex_marks:D
+ \@@_primitive:NN \middle \etex_middle:D
+ \@@_primitive:NN \muexpr \etex_muexpr:D
+ \@@_primitive:NN \mutoglue \etex_mutoglue:D
+ \@@_primitive:NN \numexpr \etex_numexpr:D
+ \@@_primitive:NN \pagediscards \etex_pagediscards:D
+ \@@_primitive:NN \parshapedimen \etex_parshapedimen:D
+ \@@_primitive:NN \parshapeindent \etex_parshapeindent:D
+ \@@_primitive:NN \parshapelength \etex_parshapelength:D
+ \@@_primitive:NN \predisplaydirection \etex_predisplaydirection:D
+ \@@_primitive:NN \protected \etex_protected:D
+ \@@_primitive:NN \readline \etex_readline:D
+ \@@_primitive:NN \savinghyphcodes \etex_savinghyphcodes:D
+ \@@_primitive:NN \savingvdiscards \etex_savingvdiscards:D
+ \@@_primitive:NN \scantokens \etex_scantokens:D
+ \@@_primitive:NN \showgroups \etex_showgroups:D
+ \@@_primitive:NN \showifs \etex_showifs:D
+ \@@_primitive:NN \showtokens \etex_showtokens:D
+ \@@_primitive:NN \splitbotmarks \etex_splitbotmarks:D
+ \@@_primitive:NN \splitdiscards \etex_splitdiscards:D
+ \@@_primitive:NN \splitfirstmarks \etex_splitfirstmarks:D
+ \@@_primitive:NN \TeXXeTstate \etex_TeXXeTstate:D
+ \@@_primitive:NN \topmarks \etex_topmarks:D
+ \@@_primitive:NN \tracingassigns \etex_tracingassigns:D
+ \@@_primitive:NN \tracinggroups \etex_tracinggroups:D
+ \@@_primitive:NN \tracingifs \etex_tracingifs:D
+ \@@_primitive:NN \tracingnesting \etex_tracingnesting:D
+ \@@_primitive:NN \tracingscantokens \etex_tracingscantokens:D
+ \@@_primitive:NN \unexpanded \etex_unexpanded:D
+ \@@_primitive:NN \unless \etex_unless:D
+ \@@_primitive:NN \widowpenalties \etex_widowpenalties:D
+ \@@_primitive:NN \pdfannot \pdftex_pdfannot:D
+ \@@_primitive:NN \pdfcatalog \pdftex_pdfcatalog:D
+ \@@_primitive:NN \pdfcompresslevel \pdftex_pdfcompresslevel:D
+ \@@_primitive:NN \pdfcolorstack \pdftex_pdfcolorstack:D
+ \@@_primitive:NN \pdfcolorstackinit \pdftex_pdfcolorstackinit:D
+ \@@_primitive:NN \pdfcreationdate \pdftex_pdfcreationdate:D
+ \@@_primitive:NN \pdfdecimaldigits \pdftex_pdfdecimaldigits:D
+ \@@_primitive:NN \pdfdest \pdftex_pdfdest:D
+ \@@_primitive:NN \pdfdestmargin \pdftex_pdfdestmargin:D
+ \@@_primitive:NN \pdfendlink \pdftex_pdfendlink:D
+ \@@_primitive:NN \pdfendthread \pdftex_pdfendthread:D
+ \@@_primitive:NN \pdffontattr \pdftex_pdffontattr:D
+ \@@_primitive:NN \pdffontname \pdftex_pdffontname:D
+ \@@_primitive:NN \pdffontobjnum \pdftex_pdffontobjnum:D
+ \@@_primitive:NN \pdfgamma \pdftex_pdfgamma:D
+ \@@_primitive:NN \pdfimageapplygamma \pdftex_pdfimageapplygamma:D
+ \@@_primitive:NN \pdfimagegamma \pdftex_pdfimagegamma:D
+ \@@_primitive:NN \pdfgentounicode \pdftex_pdfgentounicode:D
+ \@@_primitive:NN \pdfglyphtounicode \pdftex_pdfglyphtounicode:D
+ \@@_primitive:NN \pdfhorigin \pdftex_pdfhorigin:D
+ \@@_primitive:NN \pdfimagehicolor \pdftex_pdfimagehicolor:D
+ \@@_primitive:NN \pdfimageresolution \pdftex_pdfimageresolution:D
+ \@@_primitive:NN \pdfincludechars \pdftex_pdfincludechars:D
+ \@@_primitive:NN \pdfinclusioncopyfonts
+ \pdftex_pdfinclusioncopyfonts:D
+ \@@_primitive:NN \pdfinclusionerrorlevel
+ \pdftex_pdfinclusionerrorlevel:D
+ \@@_primitive:NN \pdfinfo \pdftex_pdfinfo:D
+ \@@_primitive:NN \pdflastannot \pdftex_pdflastannot:D
+ \@@_primitive:NN \pdflastlink \pdftex_pdflastlink:D
+ \@@_primitive:NN \pdflastobj \pdftex_pdflastobj:D
+ \@@_primitive:NN \pdflastxform \pdftex_pdflastxform:D
+ \@@_primitive:NN \pdflastximage \pdftex_pdflastximage:D
+ \@@_primitive:NN \pdflastximagecolordepth
+ \pdftex_pdflastximagecolordepth:D
+ \@@_primitive:NN \pdflastximagepages \pdftex_pdflastximagepages:D
+ \@@_primitive:NN \pdflinkmargin \pdftex_pdflinkmargin:D
+ \@@_primitive:NN \pdfliteral \pdftex_pdfliteral:D
+ \@@_primitive:NN \pdfminorversion \pdftex_pdfminorversion:D
+ \@@_primitive:NN \pdfnames \pdftex_pdfnames:D
+ \@@_primitive:NN \pdfobj \pdftex_pdfobj:D
+ \@@_primitive:NN \pdfobjcompresslevel
+ \pdftex_pdfobjcompresslevel:D
+ \@@_primitive:NN \pdfoutline \pdftex_pdfoutline:D
+ \@@_primitive:NN \pdfoutput \pdftex_pdfoutput:D
+ \@@_primitive:NN \pdfpageattr \pdftex_pdfpageattr:D
+ \@@_primitive:NN \pdfpagebox \pdftex_pdfpagebox:D
+ \@@_primitive:NN \pdfpageref \pdftex_pdfpageref:D
+ \@@_primitive:NN \pdfpageresources \pdftex_pdfpageresources:D
+ \@@_primitive:NN \pdfpagesattr \pdftex_pdfpagesattr:D
+ \@@_primitive:NN \pdfrefobj \pdftex_pdfrefobj:D
+ \@@_primitive:NN \pdfrefxform \pdftex_pdfrefxform:D
+ \@@_primitive:NN \pdfrefximage \pdftex_pdfrefximage:D
+ \@@_primitive:NN \pdfrestore \pdftex_pdfrestore:D
+ \@@_primitive:NN \pdfretval \pdftex_pdfretval:D
+ \@@_primitive:NN \pdfsave \pdftex_pdfsave:D
+ \@@_primitive:NN \pdfsetmatrix \pdftex_pdfsetmatrix:D
+ \@@_primitive:NN \pdfstartlink \pdftex_pdfstartlink:D
+ \@@_primitive:NN \pdfstartthread \pdftex_pdfstartthread:D
+ \@@_primitive:NN \pdfsuppressptexinfo
+ \pdftex_pdfsuppressptexinfo:D
+ \@@_primitive:NN \pdfthread \pdftex_pdfthread:D
+ \@@_primitive:NN \pdfthreadmargin \pdftex_pdfthreadmargin:D
+ \@@_primitive:NN \pdftrailer \pdftex_pdftrailer:D
+ \@@_primitive:NN \pdfuniqueresname \pdftex_pdfuniqueresname:D
+ \@@_primitive:NN \pdfvorigin \pdftex_pdfvorigin:D
+ \@@_primitive:NN \pdfxform \pdftex_pdfxform:D
+ \@@_primitive:NN \pdfxformattr \pdftex_pdfxformattr:D
+ \@@_primitive:NN \pdfxformname \pdftex_pdfxformname:D
+ \@@_primitive:NN \pdfxformresources \pdftex_pdfxformresources:D
+ \@@_primitive:NN \pdfximage \pdftex_pdfximage:D
+ \@@_primitive:NN \pdfximagebbox \pdftex_pdfximagebbox:D
+ \@@_primitive:NN \ifpdfabsdim \pdftex_ifabsdim:D
+ \@@_primitive:NN \ifpdfabsnum \pdftex_ifabsnum:D
+ \@@_primitive:NN \ifpdfprimitive \pdftex_ifprimitive:D
+ \@@_primitive:NN \pdfadjustspacing \pdftex_adjustspacing:D
+ \@@_primitive:NN \pdfcopyfont \pdftex_copyfont:D
+ \@@_primitive:NN \pdfdraftmode \pdftex_draftmode:D
+ \@@_primitive:NN \pdfeachlinedepth \pdftex_eachlinedepth:D
+ \@@_primitive:NN \pdfeachlineheight \pdftex_eachlineheight:D
+ \@@_primitive:NN \pdffilemoddate \pdftex_filemoddate:D
+ \@@_primitive:NN \pdffilesize \pdftex_filesize:D
+ \@@_primitive:NN \pdffirstlineheight \pdftex_firstlineheight:D
+ \@@_primitive:NN \pdffontexpand \pdftex_fontexpand:D
+ \@@_primitive:NN \pdffontsize \pdftex_fontsize:D
+ \@@_primitive:NN \pdfignoreddimen \pdftex_ignoreddimen:D
+ \@@_primitive:NN \pdfinsertht \pdftex_insertht:D
+ \@@_primitive:NN \pdflastlinedepth \pdftex_lastlinedepth:D
+ \@@_primitive:NN \pdflastxpos \pdftex_lastxpos:D
+ \@@_primitive:NN \pdflastypos \pdftex_lastypos:D
+ \@@_primitive:NN \pdfmapfile \pdftex_mapfile:D
+ \@@_primitive:NN \pdfmapline \pdftex_mapline:D
+ \@@_primitive:NN \pdfmdfivesum \pdftex_mdfivesum:D
+ \@@_primitive:NN \pdfnoligatures \pdftex_noligatures:D
+ \@@_primitive:NN \pdfnormaldeviate \pdftex_normaldeviate:D
+ \@@_primitive:NN \pdfpageheight \pdftex_pageheight:D
+ \@@_primitive:NN \pdfpagewidth \pdftex_pagewidth:D
+ \@@_primitive:NN \pdfpkmode \pdftex_pkmode:D
+ \@@_primitive:NN \pdfpkresolution \pdftex_pkresolution:D
+ \@@_primitive:NN \pdfprimitive \pdftex_primitive:D
+ \@@_primitive:NN \pdfprotrudechars \pdftex_protrudechars:D
+ \@@_primitive:NN \pdfpxdimen \pdftex_pxdimen:D
+ \@@_primitive:NN \pdfrandomseed \pdftex_randomseed:D
+ \@@_primitive:NN \pdfsavepos \pdftex_savepos:D
+ \@@_primitive:NN \pdfstrcmp \pdftex_strcmp:D
+ \@@_primitive:NN \pdfsetrandomseed \pdftex_setrandomseed:D
+ \@@_primitive:NN \pdfshellescape \pdftex_shellescape:D
+ \@@_primitive:NN \pdftracingfonts \pdftex_tracingfonts:D
+ \@@_primitive:NN \pdfuniformdeviate \pdftex_uniformdeviate:D
+ \@@_primitive:NN \pdftexbanner \pdftex_pdftexbanner:D
+ \@@_primitive:NN \pdftexrevision \pdftex_pdftexrevision:D
+ \@@_primitive:NN \pdftexversion \pdftex_pdftexversion:D
+ \@@_primitive:NN \efcode \pdftex_efcode:D
+ \@@_primitive:NN \ifincsname \pdftex_ifincsname:D
+ \@@_primitive:NN \leftmarginkern \pdftex_leftmarginkern:D
+ \@@_primitive:NN \letterspacefont \pdftex_letterspacefont:D
+ \@@_primitive:NN \lpcode \pdftex_lpcode:D
+ \@@_primitive:NN \quitvmode \pdftex_quitvmode:D
+ \@@_primitive:NN \rightmarginkern \pdftex_rightmarginkern:D
+ \@@_primitive:NN \rpcode \pdftex_rpcode:D
+ \@@_primitive:NN \synctex \pdftex_synctex:D
+ \@@_primitive:NN \tagcode \pdftex_tagcode:D
+ \@@_primitive:NN \mdfivesum \pdftex_mdfivesum:D
+ \@@_primitive:NN \ifprimitive \pdftex_ifprimitive:D
+ \@@_primitive:NN \primitive \pdftex_primitive:D
+ \@@_primitive:NN \shellescape \pdftex_shellescape:D
+ \@@_primitive:NN \adjustspacing \pdftex_adjustspacing:D
+ \@@_primitive:NN \copyfont \pdftex_copyfont:D
+ \@@_primitive:NN \draftmode \pdftex_draftmode:D
+ \@@_primitive:NN \expandglyphsinfont \pdftex_fontexpand:D
+ \@@_primitive:NN \ifabsdim \pdftex_ifabsdim:D
+ \@@_primitive:NN \ifabsnum \pdftex_ifabsnum:D
+ \@@_primitive:NN \ignoreligaturesinfont
+ \pdftex_ignoreligaturesinfont:D
+ \@@_primitive:NN \insertht \pdftex_insertht:D
+ \@@_primitive:NN \lastsavedboxresourceindex
+ \pdftex_pdflastxform:D
+ \@@_primitive:NN \lastsavedimageresourceindex
+ \pdftex_pdflastximage:D
+ \@@_primitive:NN \lastsavedimageresourcepages
+ \pdftex_pdflastximagepages:D
+ \@@_primitive:NN \lastxpos \pdftex_lastxpos:D
+ \@@_primitive:NN \lastypos \pdftex_lastypos:D
+ \@@_primitive:NN \normaldeviate \pdftex_normaldeviate:D
+ \@@_primitive:NN \outputmode \pdftex_pdfoutput:D
+ \@@_primitive:NN \pageheight \pdftex_pageheight:D
+ \@@_primitive:NN \pagewidth \pdftex_pagewith:D
+ \@@_primitive:NN \protrudechars \pdftex_protrudechars:D
+ \@@_primitive:NN \pxdimen \pdftex_pxdimen:D
+ \@@_primitive:NN \randomseed \pdftex_randomseed:D
+ \@@_primitive:NN \useboxresource \pdftex_pdfrefxform:D
+ \@@_primitive:NN \useimageresource \pdftex_pdfrefximage:D
+ \@@_primitive:NN \savepos \pdftex_savepos:D
+ \@@_primitive:NN \saveboxresource \pdftex_pdfxform:D
+ \@@_primitive:NN \saveimageresource \pdftex_pdfximage:D
+ \@@_primitive:NN \setrandomseed \pdftex_setrandomseed:D
+ \@@_primitive:NN \tracingfonts \pdftex_tracingfonts:D
+ \@@_primitive:NN \uniformdeviate \pdftex_uniformdeviate:D
+ \@@_primitive:NN \suppressfontnotfounderror
+ \xetex_suppressfontnotfounderror:D
+ \@@_primitive:NN \XeTeXcharclass \xetex_charclass:D
+ \@@_primitive:NN \XeTeXcharglyph \xetex_charglyph:D
+ \@@_primitive:NN \XeTeXcountfeatures \xetex_countfeatures:D
+ \@@_primitive:NN \XeTeXcountglyphs \xetex_countglyphs:D
+ \@@_primitive:NN \XeTeXcountselectors \xetex_countselectors:D
+ \@@_primitive:NN \XeTeXcountvariations \xetex_countvariations:D
+ \@@_primitive:NN \XeTeXdefaultencoding \xetex_defaultencoding:D
+ \@@_primitive:NN \XeTeXdashbreakstate \xetex_dashbreakstate:D
+ \@@_primitive:NN \XeTeXfeaturecode \xetex_featurecode:D
+ \@@_primitive:NN \XeTeXfeaturename \xetex_featurename:D
+ \@@_primitive:NN \XeTeXfindfeaturebyname
+ \xetex_findfeaturebyname:D
+ \@@_primitive:NN \XeTeXfindselectorbyname
+ \xetex_findselectorbyname:D
+ \@@_primitive:NN \XeTeXfindvariationbyname
+ \xetex_findvariationbyname:D
+ \@@_primitive:NN \XeTeXfirstfontchar \xetex_firstfontchar:D
+ \@@_primitive:NN \XeTeXfonttype \xetex_fonttype:D
+ \@@_primitive:NN \XeTeXgenerateactualtext
+ \xetex_generateactualtext:D
+ \@@_primitive:NN \XeTeXglyph \xetex_glyph:D
+ \@@_primitive:NN \XeTeXglyphbounds \xetex_glyphbounds:D
+ \@@_primitive:NN \XeTeXglyphindex \xetex_glyphindex:D
+ \@@_primitive:NN \XeTeXglyphname \xetex_glyphname:D
+ \@@_primitive:NN \XeTeXinputencoding \xetex_inputencoding:D
+ \@@_primitive:NN \XeTeXinputnormalization
+ \xetex_inputnormalization:D
+ \@@_primitive:NN \XeTeXinterchartokenstate
+ \xetex_interchartokenstate:D
+ \@@_primitive:NN \XeTeXinterchartoks \xetex_interchartoks:D
+ \@@_primitive:NN \XeTeXisdefaultselector
+ \xetex_isdefaultselector:D
+ \@@_primitive:NN \XeTeXisexclusivefeature
+ \xetex_isexclusivefeature:D
+ \@@_primitive:NN \XeTeXlastfontchar \xetex_lastfontchar:D
+ \@@_primitive:NN \XeTeXlinebreakskip \xetex_linebreakskip:D
+ \@@_primitive:NN \XeTeXlinebreaklocale \xetex_linebreaklocale:D
+ \@@_primitive:NN \XeTeXlinebreakpenalty \xetex_linebreakpenalty:D
+ \@@_primitive:NN \XeTeXOTcountfeatures \xetex_OTcountfeatures:D
+ \@@_primitive:NN \XeTeXOTcountlanguages \xetex_OTcountlanguages:D
+ \@@_primitive:NN \XeTeXOTcountscripts \xetex_OTcountscripts:D
+ \@@_primitive:NN \XeTeXOTfeaturetag \xetex_OTfeaturetag:D
+ \@@_primitive:NN \XeTeXOTlanguagetag \xetex_OTlanguagetag:D
+ \@@_primitive:NN \XeTeXOTscripttag \xetex_OTscripttag:D
+ \@@_primitive:NN \XeTeXpdffile \xetex_pdffile:D
+ \@@_primitive:NN \XeTeXpdfpagecount \xetex_pdfpagecount:D
+ \@@_primitive:NN \XeTeXpicfile \xetex_picfile:D
+ \@@_primitive:NN \XeTeXselectorname \xetex_selectorname:D
+ \@@_primitive:NN \XeTeXtracingfonts \xetex_tracingfonts:D
+ \@@_primitive:NN \XeTeXupwardsmode \xetex_upwardsmode:D
+ \@@_primitive:NN \XeTeXuseglyphmetrics \xetex_useglyphmetrics:D
+ \@@_primitive:NN \XeTeXvariation \xetex_variation:D
+ \@@_primitive:NN \XeTeXvariationdefault \xetex_variationdefault:D
+ \@@_primitive:NN \XeTeXvariationmax \xetex_variationmax:D
+ \@@_primitive:NN \XeTeXvariationmin \xetex_variationmin:D
+ \@@_primitive:NN \XeTeXvariationname \xetex_variationname:D
+ \@@_primitive:NN \XeTeXrevision \xetex_XeTeXrevision:D
+ \@@_primitive:NN \XeTeXversion \xetex_XeTeXversion:D
+ \@@_primitive:NN \alignmark \luatex_alignmark:D
+ \@@_primitive:NN \aligntab \luatex_aligntab:D
+ \@@_primitive:NN \attribute \luatex_attribute:D
+ \@@_primitive:NN \attributedef \luatex_attributedef:D
+ \@@_primitive:NN \automaticdiscretionary
+ \luatex_automaticdiscretionary:D
+ \@@_primitive:NN \automatichyphenmode
+ \luatex_automatichyphenmode:D
+ \@@_primitive:NN \automatichyphenpenalty
+ \luatex_automatichyphenpenalty:D
+ \@@_primitive:NN \begincsname \luatex_begincsname:D
+ \@@_primitive:NN \breakafterdirmode \luatex_breakafterdirmode:D
+ \@@_primitive:NN \catcodetable \luatex_catcodetable:D
+ \@@_primitive:NN \clearmarks \luatex_clearmarks:D
+ \@@_primitive:NN \crampeddisplaystyle
+ \luatex_crampeddisplaystyle:D
+ \@@_primitive:NN \crampedscriptscriptstyle
+ \luatex_crampedscriptscriptstyle:D
+ \@@_primitive:NN \crampedscriptstyle \luatex_crampedscriptstyle:D
+ \@@_primitive:NN \crampedtextstyle \luatex_crampedtextstyle:D
+ \@@_primitive:NN \directlua \luatex_directlua:D
+ \@@_primitive:NN \dviextension \luatex_dviextension:D
+ \@@_primitive:NN \dvifeedback \luatex_dvifeedback:D
+ \@@_primitive:NN \dvivariable \luatex_dvivariable:D
+ \@@_primitive:NN \etoksapp \luatex_etoksapp:D
+ \@@_primitive:NN \etokspre \luatex_etokspre:D
+ \@@_primitive:NN \explicithyphenpenalty
+ \luatex_explicithyphenpenalty:D
+ \@@_primitive:NN \expanded \luatex_expanded:D
+ \@@_primitive:NN \explicitdiscretionary
+ \luatex_explicitdiscretionary:D
+ \@@_primitive:NN \firstvalidlanguage \luatex_firstvalidlanguage:D
+ \@@_primitive:NN \fontid \luatex_fontid:D
+ \@@_primitive:NN \formatname \luatex_formatname:D
+ \@@_primitive:NN \hjcode \luatex_hjcode:D
+ \@@_primitive:NN \hpack \luatex_hpack:D
+ \@@_primitive:NN \hyphenationbounds \luatex_hyphenationbounds:D
+ \@@_primitive:NN \hyphenationmin \luatex_hyphenationmin:D
+ \@@_primitive:NN \hyphenpenaltymode \luatex_hyphenpenaltymode:D
+ \@@_primitive:NN \gleaders \luatex_gleaders:D
+ \@@_primitive:NN \initcatcodetable \luatex_initcatcodetable:D
+ \@@_primitive:NN \lastnamedcs \luatex_lastnamedcs:D
+ \@@_primitive:NN \latelua \luatex_latelua:D
+ \@@_primitive:NN \letcharcode \luatex_letcharcode:D
+ \@@_primitive:NN \luaescapestring \luatex_luaescapestring:D
+ \@@_primitive:NN \luafunction \luatex_luafunction:D
+ \@@_primitive:NN \luatexbanner \luatex_luatexbanner:D
+ \@@_primitive:NN \luatexrevision \luatex_luatexrevision:D
+ \@@_primitive:NN \luatexversion \luatex_luatexversion:D
+ \@@_primitive:NN \mathdelimitersmode \luatex_mathdelimitersmode:D
+ \@@_primitive:NN \mathdisplayskipmode
+ \luatex_mathdisplayskipmode:D
+ \@@_primitive:NN \matheqnogapstep \luatex_matheqnogapstep:D
+ \@@_primitive:NN \mathnolimitsmode \luatex_mathnolimitsmode:D
+ \@@_primitive:NN \mathoption \luatex_mathoption:D
+ \@@_primitive:NN \mathpenaltiesmode \luatex_mathpenaltiesmode:D
+ \@@_primitive:NN \mathrulesfam \luatex_mathrulesfam:D
+ \@@_primitive:NN \mathscriptsmode \luatex_mathscriptsmode:D
+ \@@_primitive:NN \mathscriptboxmode \luatex_mathscriptboxmode:D
+ \@@_primitive:NN \mathstyle \luatex_mathstyle:D
+ \@@_primitive:NN \mathsurroundmode \luatex_mathsurroundmode:D
+ \@@_primitive:NN \mathsurroundskip \luatex_mathsurroundskip:D
+ \@@_primitive:NN \nohrule \luatex_nohrule:D
+ \@@_primitive:NN \nokerns \luatex_nokerns:D
+ \@@_primitive:NN \noligs \luatex_noligs:D
+ \@@_primitive:NN \nospaces \luatex_nospaces:D
+ \@@_primitive:NN \novrule \luatex_novrule:D
+ \@@_primitive:NN \outputbox \luatex_outputbox:D
+ \@@_primitive:NN \pagebottomoffset \luatex_pagebottomoffset:D
+ \@@_primitive:NN \pageleftoffset \luatex_pageleftoffset:D
+ \@@_primitive:NN \pagerightoffset \luatex_pagerightoffset:D
+ \@@_primitive:NN \pagetopoffset \luatex_pagetopoffset:D
+ \@@_primitive:NN \pdfextension \luatex_pdfextension:D
+ \@@_primitive:NN \pdffeedback \luatex_pdffeedback:D
+ \@@_primitive:NN \pdfvariable \luatex_pdfvariable:D
+ \@@_primitive:NN \postexhyphenchar \luatex_postexhyphenchar:D
+ \@@_primitive:NN \posthyphenchar \luatex_posthyphenchar:D
+ \@@_primitive:NN \prebinoppenalty \luatex_prebinoppenalty:D
+ \@@_primitive:NN \predisplaygapfactor
+ \luatex_predisplaygapfactor:D
+ \@@_primitive:NN \preexhyphenchar \luatex_preexhyphenchar:D
+ \@@_primitive:NN \prehyphenchar \luatex_prehyphenchar:D
+ \@@_primitive:NN \prerelpenalty \luatex_prerelpenalty:D
+ \@@_primitive:NN \savecatcodetable \luatex_savecatcodetable:D
+ \@@_primitive:NN \scantextokens \luatex_scantextokens:D
+ \@@_primitive:NN \setfontid \luatex_setfontid:D
+ \@@_primitive:NN \shapemode \luatex_shapemode:D
+ \@@_primitive:NN \suppressifcsnameerror
+ \luatex_suppressifcsnameerror:D
+ \@@_primitive:NN \suppresslongerror \luatex_suppresslongerror:D
+ \@@_primitive:NN \suppressmathparerror
+ \luatex_suppressmathparerror:D
+ \@@_primitive:NN \suppressoutererror \luatex_suppressoutererror:D
+ \@@_primitive:NN \suppressprimitiveerror
+ \luatex_suppressprimitiveerror:D
+ \@@_primitive:NN \toksapp \luatex_toksapp:D
+ \@@_primitive:NN \tokspre \luatex_tokspre:D
+ \@@_primitive:NN \tpack \luatex_tpack:D
+ \@@_primitive:NN \vpack \luatex_vpack:D
+ \@@_primitive:NN \bodydir \luatex_bodydir:D
+ \@@_primitive:NN \boxdir \luatex_boxdir:D
+ \@@_primitive:NN \leftghost \luatex_leftghost:D
+ \@@_primitive:NN \linedir \luatex_linedir:D
+ \@@_primitive:NN \localbrokenpenalty \luatex_localbrokenpenalty:D
+ \@@_primitive:NN \localinterlinepenalty
+ \luatex_localinterlinepenalty:D
+ \@@_primitive:NN \localleftbox \luatex_localleftbox:D
+ \@@_primitive:NN \localrightbox \luatex_localrightbox:D
+ \@@_primitive:NN \mathdir \luatex_mathdir:D
+ \@@_primitive:NN \pagedir \luatex_pagedir:D
+ \@@_primitive:NN \pardir \luatex_pardir:D
+ \@@_primitive:NN \rightghost \luatex_rightghost:D
+ \@@_primitive:NN \textdir \luatex_textdir:D
+ \@@_primitive:NN \Uchar \utex_char:D
+ \@@_primitive:NN \Ucharcat \utex_charcat:D
+ \@@_primitive:NN \Udelcode \utex_delcode:D
+ \@@_primitive:NN \Udelcodenum \utex_delcodenum:D
+ \@@_primitive:NN \Udelimiter \utex_delimiter:D
+ \@@_primitive:NN \Udelimiterover \utex_delimiterover:D
+ \@@_primitive:NN \Udelimiterunder \utex_delimiterunder:D
+ \@@_primitive:NN \Uhextensible \utex_hextensible:D
+ \@@_primitive:NN \Umathaccent \utex_mathaccent:D
+ \@@_primitive:NN \Umathaxis \utex_mathaxis:D
+ \@@_primitive:NN \Umathbinbinspacing \utex_binbinspacing:D
+ \@@_primitive:NN \Umathbinclosespacing \utex_binclosespacing:D
+ \@@_primitive:NN \Umathbininnerspacing \utex_bininnerspacing:D
+ \@@_primitive:NN \Umathbinopenspacing \utex_binopenspacing:D
+ \@@_primitive:NN \Umathbinopspacing \utex_binopspacing:D
+ \@@_primitive:NN \Umathbinordspacing \utex_binordspacing:D
+ \@@_primitive:NN \Umathbinpunctspacing \utex_binpunctspacing:D
+ \@@_primitive:NN \Umathbinrelspacing \utex_binrelspacing:D
+ \@@_primitive:NN \Umathchar \utex_mathchar:D
+ \@@_primitive:NN \Umathcharclass \utex_mathcharclass:D
+ \@@_primitive:NN \Umathchardef \utex_mathchardef:D
+ \@@_primitive:NN \Umathcharfam \utex_mathcharfam:D
+ \@@_primitive:NN \Umathcharnum \utex_mathcharnum:D
+ \@@_primitive:NN \Umathcharnumdef \utex_mathcharnumdef:D
+ \@@_primitive:NN \Umathcharslot \utex_mathcharslot:D
+ \@@_primitive:NN \Umathclosebinspacing \utex_closebinspacing:D
+ \@@_primitive:NN \Umathcloseclosespacing
+ \utex_closeclosespacing:D
+ \@@_primitive:NN \Umathcloseinnerspacing
+ \utex_closeinnerspacing:D
+ \@@_primitive:NN \Umathcloseopenspacing \utex_closeopenspacing:D
+ \@@_primitive:NN \Umathcloseopspacing \utex_closeopspacing:D
+ \@@_primitive:NN \Umathcloseordspacing \utex_closeordspacing:D
+ \@@_primitive:NN \Umathclosepunctspacing
+ \utex_closepunctspacing:D
+ \@@_primitive:NN \Umathcloserelspacing \utex_closerelspacing:D
+ \@@_primitive:NN \Umathcode \utex_mathcode:D
+ \@@_primitive:NN \Umathcodenum \utex_mathcodenum:D
+ \@@_primitive:NN \Umathconnectoroverlapmin
+ \utex_connectoroverlapmin:D
+ \@@_primitive:NN \Umathfractiondelsize \utex_fractiondelsize:D
+ \@@_primitive:NN \Umathfractiondenomdown
+ \utex_fractiondenomdown:D
+ \@@_primitive:NN \Umathfractiondenomvgap
+ \utex_fractiondenomvgap:D
+ \@@_primitive:NN \Umathfractionnumup \utex_fractionnumup:D
+ \@@_primitive:NN \Umathfractionnumvgap \utex_fractionnumvgap:D
+ \@@_primitive:NN \Umathfractionrule \utex_fractionrule:D
+ \@@_primitive:NN \Umathinnerbinspacing \utex_innerbinspacing:D
+ \@@_primitive:NN \Umathinnerclosespacing
+ \utex_innerclosespacing:D
+ \@@_primitive:NN \Umathinnerinnerspacing
+ \utex_innerinnerspacing:D
+ \@@_primitive:NN \Umathinneropenspacing \utex_inneropenspacing:D
+ \@@_primitive:NN \Umathinneropspacing \utex_inneropspacing:D
+ \@@_primitive:NN \Umathinnerordspacing \utex_innerordspacing:D
+ \@@_primitive:NN \Umathinnerpunctspacing
+ \utex_innerpunctspacing:D
+ \@@_primitive:NN \Umathinnerrelspacing \utex_innerrelspacing:D
+ \@@_primitive:NN \Umathlimitabovebgap \utex_limitabovebgap:D
+ \@@_primitive:NN \Umathlimitabovekern \utex_limitabovekern:D
+ \@@_primitive:NN \Umathlimitabovevgap \utex_limitabovevgap:D
+ \@@_primitive:NN \Umathlimitbelowbgap \utex_limitbelowbgap:D
+ \@@_primitive:NN \Umathlimitbelowkern \utex_limitbelowkern:D
+ \@@_primitive:NN \Umathlimitbelowvgap \utex_limitbelowvgap:D
+ \@@_primitive:NN \Umathnolimitsubfactor \utex_nolimitsubfactor:D
+ \@@_primitive:NN \Umathnolimitsupfactor \utex_nolimitsupfactor:D
+ \@@_primitive:NN \Umathopbinspacing \utex_opbinspacing:D
+ \@@_primitive:NN \Umathopclosespacing \utex_opclosespacing:D
+ \@@_primitive:NN \Umathopenbinspacing \utex_openbinspacing:D
+ \@@_primitive:NN \Umathopenclosespacing \utex_openclosespacing:D
+ \@@_primitive:NN \Umathopeninnerspacing \utex_openinnerspacing:D
+ \@@_primitive:NN \Umathopenopenspacing \utex_openopenspacing:D
+ \@@_primitive:NN \Umathopenopspacing \utex_openopspacing:D
+ \@@_primitive:NN \Umathopenordspacing \utex_openordspacing:D
+ \@@_primitive:NN \Umathopenpunctspacing \utex_openpunctspacing:D
+ \@@_primitive:NN \Umathopenrelspacing \utex_openrelspacing:D
+ \@@_primitive:NN \Umathoperatorsize \utex_operatorsize:D
+ \@@_primitive:NN \Umathopinnerspacing \utex_opinnerspacing:D
+ \@@_primitive:NN \Umathopopenspacing \utex_opopenspacing:D
+ \@@_primitive:NN \Umathopopspacing \utex_opopspacing:D
+ \@@_primitive:NN \Umathopordspacing \utex_opordspacing:D
+ \@@_primitive:NN \Umathoppunctspacing \utex_oppunctspacing:D
+ \@@_primitive:NN \Umathoprelspacing \utex_oprelspacing:D
+ \@@_primitive:NN \Umathordbinspacing \utex_ordbinspacing:D
+ \@@_primitive:NN \Umathordclosespacing \utex_ordclosespacing:D
+ \@@_primitive:NN \Umathordinnerspacing \utex_ordinnerspacing:D
+ \@@_primitive:NN \Umathordopenspacing \utex_ordopenspacing:D
+ \@@_primitive:NN \Umathordopspacing \utex_ordopspacing:D
+ \@@_primitive:NN \Umathordordspacing \utex_ordordspacing:D
+ \@@_primitive:NN \Umathordpunctspacing \utex_ordpunctspacing:D
+ \@@_primitive:NN \Umathordrelspacing \utex_ordrelspacing:D
+ \@@_primitive:NN \Umathoverbarkern \utex_overbarkern:D
+ \@@_primitive:NN \Umathoverbarrule \utex_overbarrule:D
+ \@@_primitive:NN \Umathoverbarvgap \utex_overbarvgap:D
+ \@@_primitive:NN \Umathoverdelimiterbgap
+ \utex_overdelimiterbgap:D
+ \@@_primitive:NN \Umathoverdelimitervgap
+ \utex_overdelimitervgap:D
+ \@@_primitive:NN \Umathpunctbinspacing \utex_punctbinspacing:D
+ \@@_primitive:NN \Umathpunctclosespacing
+ \utex_punctclosespacing:D
+ \@@_primitive:NN \Umathpunctinnerspacing
+ \utex_punctinnerspacing:D
+ \@@_primitive:NN \Umathpunctopenspacing \utex_punctopenspacing:D
+ \@@_primitive:NN \Umathpunctopspacing \utex_punctopspacing:D
+ \@@_primitive:NN \Umathpunctordspacing \utex_punctordspacing:D
+ \@@_primitive:NN \Umathpunctpunctspacing\utex_punctpunctspacing:D
+ \@@_primitive:NN \Umathpunctrelspacing \utex_punctrelspacing:D
+ \@@_primitive:NN \Umathquad \utex_quad:D
+ \@@_primitive:NN \Umathradicaldegreeafter
+ \utex_radicaldegreeafter:D
+ \@@_primitive:NN \Umathradicaldegreebefore
+ \utex_radicaldegreebefore:D
+ \@@_primitive:NN \Umathradicaldegreeraise
+ \utex_radicaldegreeraise:D
+ \@@_primitive:NN \Umathradicalkern \utex_radicalkern:D
+ \@@_primitive:NN \Umathradicalrule \utex_radicalrule:D
+ \@@_primitive:NN \Umathradicalvgap \utex_radicalvgap:D
+ \@@_primitive:NN \Umathrelbinspacing \utex_relbinspacing:D
+ \@@_primitive:NN \Umathrelclosespacing \utex_relclosespacing:D
+ \@@_primitive:NN \Umathrelinnerspacing \utex_relinnerspacing:D
+ \@@_primitive:NN \Umathrelopenspacing \utex_relopenspacing:D
+ \@@_primitive:NN \Umathrelopspacing \utex_relopspacing:D
+ \@@_primitive:NN \Umathrelordspacing \utex_relordspacing:D
+ \@@_primitive:NN \Umathrelpunctspacing \utex_relpunctspacing:D
+ \@@_primitive:NN \Umathrelrelspacing \utex_relrelspacing:D
+ \@@_primitive:NN \Umathskewedfractionhgap
+ \utex_skewedfractionhgap:D
+ \@@_primitive:NN \Umathskewedfractionvgap
+ \utex_skewedfractionvgap:D
+ \@@_primitive:NN \Umathspaceafterscript \utex_spaceafterscript:D
+ \@@_primitive:NN \Umathstackdenomdown \utex_stackdenomdown:D
+ \@@_primitive:NN \Umathstacknumup \utex_stacknumup:D
+ \@@_primitive:NN \Umathstackvgap \utex_stackvgap:D
+ \@@_primitive:NN \Umathsubshiftdown \utex_subshiftdown:D
+ \@@_primitive:NN \Umathsubshiftdrop \utex_subshiftdrop:D
+ \@@_primitive:NN \Umathsubsupshiftdown \utex_subsupshiftdown:D
+ \@@_primitive:NN \Umathsubsupvgap \utex_subsupvgap:D
+ \@@_primitive:NN \Umathsubtopmax \utex_subtopmax:D
+ \@@_primitive:NN \Umathsupbottommin \utex_supbottommin:D
+ \@@_primitive:NN \Umathsupshiftdrop \utex_supshiftdrop:D
+ \@@_primitive:NN \Umathsupshiftup \utex_supshiftup:D
+ \@@_primitive:NN \Umathsupsubbottommax \utex_supsubbottommax:D
+ \@@_primitive:NN \Umathunderbarkern \utex_underbarkern:D
+ \@@_primitive:NN \Umathunderbarrule \utex_underbarrule:D
+ \@@_primitive:NN \Umathunderbarvgap \utex_underbarvgap:D
+ \@@_primitive:NN \Umathunderdelimiterbgap
+ \utex_underdelimiterbgap:D
+ \@@_primitive:NN \Umathunderdelimitervgap
+ \utex_underdelimitervgap:D
+ \@@_primitive:NN \Unosubscript \utex_nosubscript:D
+ \@@_primitive:NN \Unosuperscript \utex_nosuperscript:D
+ \@@_primitive:NN \Uoverdelimiter \utex_overdelimiter:D
+ \@@_primitive:NN \Uradical \utex_radical:D
+ \@@_primitive:NN \Uroot \utex_root:D
+ \@@_primitive:NN \Uskewed \utex_skewed:D
+ \@@_primitive:NN \Uskewedwithdelims \utex_skewedwithdelims:D
+ \@@_primitive:NN \Ustack \utex_stack:D
+ \@@_primitive:NN \Ustartdisplaymath \utex_startdisplaymath:D
+ \@@_primitive:NN \Ustartmath \utex_startmath:D
+ \@@_primitive:NN \Ustopdisplaymath \utex_stopdisplaymath:D
+ \@@_primitive:NN \Ustopmath \utex_stopmath:D
+ \@@_primitive:NN \Usubscript \utex_subscript:D
+ \@@_primitive:NN \Usuperscript \utex_superscript:D
+ \@@_primitive:NN \Uunderdelimiter \utex_underdelimiter:D
+ \@@_primitive:NN \Uvextensible \utex_vextensible:D
+ \@@_primitive:NN \autospacing \ptex_autospacing:D
+ \@@_primitive:NN \autoxspacing \ptex_autoxspacing:D
+ \@@_primitive:NN \dtou \ptex_dtou:D
+ \@@_primitive:NN \epTeXinputencoding \ptex_inputencoding:D
+ \@@_primitive:NN \epTeXversion \ptex_epTeXversion:D
+ \@@_primitive:NN \euc \ptex_euc:D
+ \@@_primitive:NN \ifdbox \ptex_ifdbox:D
+ \@@_primitive:NN \ifddir \ptex_ifddir:D
+ \@@_primitive:NN \ifmdir \ptex_ifmdir:D
+ \@@_primitive:NN \iftbox \ptex_iftbox:D
+ \@@_primitive:NN \iftdir \ptex_iftdir:D
+ \@@_primitive:NN \ifybox \ptex_ifybox:D
+ \@@_primitive:NN \ifydir \ptex_ifydir:D
+ \@@_primitive:NN \inhibitglue \ptex_inhibitglue:D
+ \@@_primitive:NN \inhibitxspcode \ptex_inhibitxspcode:D
+ \@@_primitive:NN \jcharwidowpenalty \ptex_jcharwidowpenalty:D
+ \@@_primitive:NN \jfam \ptex_jfam:D
+ \@@_primitive:NN \jfont \ptex_jfont:D
+ \@@_primitive:NN \jis \ptex_jis:D
+ \@@_primitive:NN \kanjiskip \ptex_kanjiskip:D
+ \@@_primitive:NN \kansuji \ptex_kansuji:D
+ \@@_primitive:NN \kansujichar \ptex_kansujichar:D
+ \@@_primitive:NN \kcatcode \ptex_kcatcode:D
+ \@@_primitive:NN \kuten \ptex_kuten:D
+ \@@_primitive:NN \noautospacing \ptex_noautospacing:D
+ \@@_primitive:NN \noautoxspacing \ptex_noautoxspacing:D
+ \@@_primitive:NN \postbreakpenalty \ptex_postbreakpenalty:D
+ \@@_primitive:NN \prebreakpenalty \ptex_prebreakpenalty:D
+ \@@_primitive:NN \ptexminorversion \ptex_ptexminorversion:D
+ \@@_primitive:NN \ptexrevision \ptex_ptexrevision:D
+ \@@_primitive:NN \ptexversion \ptex_ptexversion:D
+ \@@_primitive:NN \showmode \ptex_showmode:D
+ \@@_primitive:NN \sjis \ptex_sjis:D
+ \@@_primitive:NN \tate \ptex_tate:D
+ \@@_primitive:NN \tbaselineshift \ptex_tbaselineshift:D
+ \@@_primitive:NN \tfont \ptex_tfont:D
+ \@@_primitive:NN \xkanjiskip \ptex_xkanjiskip:D
+ \@@_primitive:NN \xspcode \ptex_xspcode:D
+ \@@_primitive:NN \ybaselineshift \ptex_ybaselineshift:D
+ \@@_primitive:NN \yoko \ptex_yoko:D
+ \@@_primitive:NN \disablecjktoken \uptex_disablecjktoken:D
+ \@@_primitive:NN \enablecjktoken \uptex_enablecjktoken:D
+ \@@_primitive:NN \forcecjktoken \uptex_forcecjktoken:D
+ \@@_primitive:NN \kchar \uptex_kchar:D
+ \@@_primitive:NN \kchardef \uptex_kchardef:D
+ \@@_primitive:NN \kuten \uptex_kuten:D
+ \@@_primitive:NN \ucs \uptex_ucs:D
+ \@@_primitive:NN \uptexrevision \uptex_uptexrevision:D
+ \@@_primitive:NN \uptexversion \uptex_uptexversion:D
+ }
+ }
+ \@@_primitives:
+\tex_endgroup:D
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3obsolete.txt b/macros/latex/contrib/l3kernel/l3obsolete.txt
new file mode 100644
index 0000000000..8706c0aba3
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3obsolete.txt
@@ -0,0 +1,639 @@
+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.
+They will be removed from the sources at the end of the given year.
+
+--------------------------------------
+Function Expires end
+--------------------------------------
+\box_gset_eq_clear:cc 2020
+\box_gset_eq_clear:cN 2020
+\box_gset_eq_clear:Nc 2020
+\box_gset_eq_clear:NN 2020
+\box_set_eq_clear:cc 2020
+\box_set_eq_clear:cN 2020
+\box_set_eq_clear:Nc 2020
+\box_set_eq_clear:NN 2020
+\c_zero 2019
+\c_one 2019
+\c_two 2019
+\c_three 2019
+\c_four 2019
+\c_five 2019
+\c_six 2019
+\c_seven 2019
+\c_eight 2019
+\c_nine 2019
+\c_ten 2019
+\c_eleven 2019
+\c_twelve 2019
+\c_thirteen 2019
+\c_fourteen 2019
+\c_fifteen 2019
+\c_sixteen 2019
+\c_thirty_two 2019
+\c_one_hundred 2019
+\c_two_hundred_fifty_five 2019
+\c_two_hundred_fifty_six 2019
+\c_one_thousand 2019
+\c_ten_thousand 2019
+\c_term_ior 2020
+\etex_....:D 2019
+\hbox_unpack_clear:c 2020
+\hbox_unpack_clear:N 2020
+\lua_escape_x:n 2019
+\lua_now_x:n 2019
+\lua_shipout_x:n 2019
+\luatex_...:D 2019
+\msg_interrupt:nn 2019
+\msg_log:n 2019
+\msg_term:n 2019
+\pdftex_...:D 2019
+\ptex_...:D 2019
+\str_case_x:nn 2019
+\str_case_x:nnF 2019
+\str_case_x:nnT 2019
+\str_case_x:nnTF 2019
+\str_if_eq_x:nnF 2019
+\str_if_eq_x:nnT 2019
+\str_if_eq_x:nnTF 2019
+\str_if_eq_x_p:nn 2019
+\tl_gset_from_file:cnn 2020
+\tl_gset_from_file:Nnn 2020
+\tl_gset_from_file_x:cnn 2020
+\tl_gset_from_file_x:Nnn 2020
+\tl_show_analysis:N 2019
+\tl_show_analysis:n 2019
+\tl_set_from_file:cnn 2020
+\tl_set_from_file:Nnn 2020
+\tl_set_from_file_x:cnn 2020
+\tl_set_from_file_x:Nnn 2020
+\token_get_arg_spec:N 2020
+\token_get_prefix_spec:N 2020
+\token_get_replacement_spec:N 2020
+\uptex_...:D 2019
+\utex_...:D 2019
+\vbox_unpack_clear:c 2020
+\vbox_unpack_clear:N 2020
+\xetex_...:D 2019
+--------------------------------------
+
+Removed functions and variables
+===============================
+
+The 'Date removed' column details when the functions were
+removed from the sources.
+
+----------------------------------------------------------
+Function Date removed
+----------------------------------------------------------
+\box_resize:cnn 2018-12-27
+\box_resize:Nnn 2018-12-27
+\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_empty_toks 2011-09-08
+\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_other_char_token 2011-09-08
+\c_pdftex_is_engine_bool 2011-12-30
+\c_string_cctab 2012-06-30
+\c_undefined_fp 2014-05-28
+\c_xetex_is_engine_bool 2011-12-30
+\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
+\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
+\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
+\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:xxx 2013-01-08
+\msg_log:x 2013-01-08
+\msg_newline: 2013-01-08
+\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_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
+\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:nnn 2015-07-14
+\str_if_eq_p:xx 2013-01-08
+\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_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_tail:w 2013-01-08
+\tl_to_lowercase:n 2018-03-05
+\tl_to_uppercase:n 2018-03-05
+\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
+\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
+\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
+\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
+----------------------------------------------------------
diff --git a/macros/latex/contrib/l3kernel/l3prefixes.csv b/macros/latex/contrib/l3kernel/l3prefixes.csv
new file mode 100644
index 0000000000..1e28ce8fbf
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3prefixes.csv
@@ -0,0 +1,183 @@
+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://bitbucket.org/cgnieder/acro/,git@bitbucket.org:cgnieder/acro.git,https://bitbucket.org/cgnieder/acro/issues,2013-03-16,2013-03-16,
+algobox,algobox,Julien Rivaud,,,,2018-06-13,2018-06-13,
+alignment,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+alloc,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Internal only but reserved
+ampersand,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+arch,archaeologie,Lukas C. Bossert,http://www.biblatex-archaeologie.de,https://github.com/LukasCBossert/biblatex-archaeologie.git,https://github.com/LukasCBossert/biblatex-archaeologie/issues,2017-03-24,2017-03-24,
+array,hobby,Andrew Stacey,,,,2013-03-16,2013-03-16,
+atsign,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+backend,l3backend,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-04,2019-06-04,
+backslash,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+bool,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+box,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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,
+catcode,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+cctab,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+cellprops,cellprops,Julien Rivaud,,,,2018-06-13,2018-06-13,
+char,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+chemformula,chemmacros,Clemens Niederberger,https://bitbucket.org/cgnieder/chemmacros/,git@bitbucket.org:cgnieder/chemmacros.git,https://bitbucket.org/cgnieder/chemmacros/issues,2013-03-16,2013-03-16,
+chemmacros,chemmacros,Clemens Niederberger,https://bitbucket.org/cgnieder/chemmacros/,git@bitbucket.org:cgnieder/chemmacros.git,https://bitbucket.org/cgnieder/chemmacros/issues,2013-03-16,2013-03-16,
+chemnum,chemnum,Clemens Niederberger,https://bitbucket.org/cgnieder/chemnum/,git@bitbucket.org:cgnieder/chemnum.git,https://bitbucket.org/cgnieder/chemnum/issues,2013-03-16,2013-03-16,
+chk,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Currently internal-only but reserved
+circumflex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+classics,classics,Eduardo C. Lourenço de Lima,,,,2013-03-16,2013-03-16,
+clist,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+codedoc,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Somewhat experimental: may change
+coffin,"l3kernel,xcoffins",The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+colon,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+color,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 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,
+deriv,derivative,Simon Jensen,,,,2019-07-24,2019-07-24,
+dim,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+document,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+dollar,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+driver,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+else,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+empty,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+enotez,enotez,Clemens Niederberger,https://bitbucket.org/cgnieder/enotez/,git@bitbucket.org:cgnieder/enotez.git,https://bitbucket.org/cgnieder/enotez/issues,2013-03-16,2013-03-16,
+etex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+exp,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+expl,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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,
+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,
+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,2018-06-14,
+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,2018-06-14,
+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,2018-06-14,
+fi,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+file,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+flag,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2017-02-13,
+fltr,newlfm,Paul Thomson,,,,2013-01-29,2013-01-29,
+fmdug,dashundergaps,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex.git,https://github.com/FrankMittelbach/fmitex/issues,2018-06-24,2018-06-24,
+fmwao,widows-and-orphans,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex.git,https://github.com/FrankMittelbach/fmitex/issues,2018-09-26,2018-09-26,
+fnpct,fnpct,Clemens Niederberger,https://bitbucket.org/cgnieder/fnpct/,git@bitbucket.org:cgnieder/fnpct.git,https://bitbucket.org/cgnieder/fnpct/issues,2013-03-16,2013-03-16,
+fontsizes,fontsizes,Julien Rivaud,,,,,2018-06-13,
+fontspec,fontspec,Will Robertson,https://github.com/wspr/fontspec,https://github.com/wspr/fontspec.git,https://github.com/wspr/fontspec/issues,2013-03-16,2013-03-16,
+fp,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+galley,l3galley,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+gatherenum,gatherenum,Julien Rivaud,,,,2018-06-13,2018-06-14,
+ghsystem,chemmacros,Clemens Niederberger,https://bitbucket.org/cgnieder/chemmacros/,git@bitbucket.org:cgnieder/chemmacros.git,https://bitbucket.org/cgnieder/chemmacros/issues,2013-03-16,2013-03-16,
+graph,lt3graph,Michiel Helvensteijn,,,,2014-02-05,2014-02-05,
+group,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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,
+hash,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+hbox,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+hcoffin,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+hobete,hobete,Tobias Görlach,http://www.disk0s1.de,,,2012-11-07,2012-11-07,
+hook,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-03,2019-06-03,
+if,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+inf,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+insert,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+int,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+intarray,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-04-06,2018-04-06,
+ior,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+iow,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+kernel,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+keys,"l3kernel,l3keys2e",The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+keyval,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+kgl,kantlipsum,Enrico Gregorio,,,,2013-03-16,2013-03-16,
+left,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+lua,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+luatex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+mark,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+math,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+max,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+mcrule,multicolrule,Karl Hagen,https://github.com/polysyllabic/multicolrule,https://github.com/polysyllabic/multicolrule.git,https://github.com/polysyllabic/multicolrule/issues,2018-12-24,2018-12-24,
+mhchem,mhchem,Martin Hensel,,,,2014-02-05,2014-02-05,
+minus,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+mix,media9,Alexander Grahn,,,,2013-01-18,2013-01-18,
+mode,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+muskip,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+nan,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+nil,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+no,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+ocgbase,ocgx2,Alexander Grahn,,,,2016-02-26,2016-02-26,
+ocgxii,ocgx2,Alexander Grahn,,,,2016-02-26,2016-02-26,
+one,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+or,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+parameter,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+pbs,media9,Alexander Grahn,,,,2016-02-26,2016-02-26,
+pdf,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+pdftex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+peek,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+percent,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+pi,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+pkgploader,pkgploader,Michiel Helvensteijn,,,,2014-02-05,2014-02-05,
+prg,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+primargs,morewrites,Bruno Le Floch,https://github.com/blefloch/latex-morewrites,https://github.com/blefloch/latex-morewrites.git,https://github.com/blefloch/latex-morewrites/issues,2013-03-16,2015-09-22,
+prop,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+pseudo,pseudo,Magnus Lie Hetland,https://github.com/mlhetland/pseudo.sty,https://github.com/mlhetland/pseudo.sty.git,https://github.com/mlhetland/pseudo.sty/issues,2019-06-24,2019-06-24,
+ptex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+quark,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+randomwalk,randomwalk,Bruno Le Floch,https://github.com/blefloch/latex-randomwalk,https://github.com/blefloch/latex-randomwalk.git,https://github.com/blefloch/latex-randomwalk/issues,2013-03-16,2015-09-22,
+recursion,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+regex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2018-04-06,
+reverse,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+right,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+rivbook,rivbook,Julien Rivaud,,,,2018-06-13,2018-06-14,
+rivmath,rivmath,Julien Rivaud,,,,2018-06-13,2018-06-13,
+scan,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+seq,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+siunitx,siunitx,Joseph Wright,https://github.com/josephwright/siunitx,https://github.com/josephwright/siunitx.git,https://github.com/josephwright/siunitx/issues,2012-11-04,2012-11-04,
+skip,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+sort,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2017-02-13,
+space,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+str,"l3kernel,l3str,l3str-format",The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+substances,substances,Clemens Niederberger,https://bitbucket.org/cgnieder/substances/,git@bitbucket.org:cgnieder/substances.git,https://bitbucket.org/cgnieder/substances/issues,2013-03-16,2013-03-16,
+sys,l3kernel,The LaTeX3 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,
+tasks,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,
+term,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tl,"l3kernel,l3tl-analysis,l3tl-build",The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+tmpa,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tmpb,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+tag,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02,
+token,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+use,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+utex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28,
+vbox,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+vcoffin,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28,
+withargs,withargs,Michiel Helvensteijn,,,,2014-02-05,2014-02-05,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+xfrac,xfrac,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+xparse,xparse,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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,,,,2016-02-26,2016-02-26,
+xtemplate,xtemplate,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
+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 LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
+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,
+zxjt,zxjatype,Takayuki Yato,,,,2013-03-16,2013-03-16,
diff --git a/macros/latex/contrib/l3kernel/l3prefixes.pdf b/macros/latex/contrib/l3kernel/l3prefixes.pdf
new file mode 100644
index 0000000000..e74cb29e43
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3prefixes.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3prefixes.tex b/macros/latex/contrib/l3kernel/l3prefixes.tex
new file mode 100644
index 0000000000..06b58ea660
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3prefixes.tex
@@ -0,0 +1,83 @@
+\iffalse meta-comment
+
+File:l3prefixes.tex
+
+Copyright (C) 2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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. 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} \ No newline at end of file
diff --git a/macros/latex/contrib/l3kernel/l3prg.dtx b/macros/latex/contrib/l3kernel/l3prg.dtx
new file mode 100644
index 0000000000..5e4fa7732c
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3prg.dtx
@@ -0,0 +1,1625 @@
+% \iffalse meta-comment
+%
+%% File: l3prg.dtx
+%
+% Copyright (C) 2005-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Control structures^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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 = 2012-02-06]
+% {
+% \prg_new_conditional:Npnn, \prg_set_conditional:Npnn,
+% \prg_new_conditional:Nnn, \prg_set_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_new_protected_conditional:Nnn, \prg_set_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.
+%
+% \begin{function}{\prg_new_eq_conditional:NNn, \prg_set_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}
+% {
+% \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}[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}[added = 2012-02-09, updated = 2015-08-01]{\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 = 2015-08-03]{\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}
+%
+% \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 firsts 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 firsts 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 firsts 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 firsts 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}
+%
+% \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: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 \cs{prop_get:Nn}), 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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \subsection{Primitive conditionals}
+%
+% \begin{macro}{\if_bool:N}
+% \begin{macro}{\if_predicate:w}
+% Those two primitive \TeX{} conditionals are synonyms.
+% \begin{macrocode}
+\cs_new_eq:NN \if_bool:N \tex_ifodd:D
+\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}
+% {
+% \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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \bool_set_true:N #1
+ { \cs_set_eq:NN #1 \c_true_bool }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \bool_set_false:N #1
+ { \cs_set_eq:NN #1 \c_false_bool }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \bool_gset_true:N #1
+ { \cs_gset_eq:NN #1 \c_true_bool }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \bool_set:Nn #1#2
+ {
+ \exp_last_unbraced:NNNf
+ \tex_chardef:D #1 = { \bool_if_p:n {#2} }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}[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}{\bool_show:n, \bool_log:n}
+% \begin{macro}{\@@_to_str:n}
+% Show the truth value of the boolean, as \texttt{true} or
+% \texttt{false}.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_show:n
+ { \msg_show_eval:Nn \@@_to_str:n }
+\cs_new_protected:Npn \bool_log:n
+ { \msg_log_eval:Nn \@@_to_str:n }
+\cs_new:Npn \@@_to_str:n #1
+ { \bool_if:nTF {#1} { true } { false } }
+% \end{macrocode}
+% \end{macro}
+% \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
+ { \exp_args:Nx #1 { \token_to_str:N #2 = \@@_to_str:n {#2} } }
+ }
+% \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. 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 {#2}
+% \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
+ {
+ \quark_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
+ {
+ \quark_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}
+%
+% \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:
+ \__kernel_msg_expandable_error:nn { kernel } { 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 issues 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 code documented only in Appendix~D of
+% \emph{The \TeX{}book}\dots
+% 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}
+\cs_new:Npn \group_align_safe_begin:
+ { \if_int_compare:w \if_false: { \fi: `} = \c_zero_int \fi: }
+\cs_new:Npn \group_align_safe_end:
+ { \if_int_compare:w `{ = \c_zero_int } \fi: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=prg>
+% \end{macrocode}
+%
+% \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} as in format mode these are needed within
+% \pkg{l3alloc}.
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3prop.dtx b/macros/latex/contrib/l3kernel/l3prop.dtx
new file mode 100644
index 0000000000..d79073065d
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3prop.dtx
@@ -0,0 +1,1262 @@
+% \iffalse meta-comment
+%
+%% File: l3prop.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Property lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \LaTeX3 implements a \enquote{property list} data type, which contain
+% an unordered list of entries each of which consists of a \meta{key} and
+% an associated \meta{value}. The \meta{key} and \meta{value} may both be
+% any \meta{balanced text}. It is possible to map functions to property lists
+% such that the function is applied 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:nn}.
+%
+% Property lists are intended for storing key-based information for use within
+% code. This is in contrast to key--value lists, which are a form of
+% \emph{input} parsed by the \pkg{keys} module.
+%
+% \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 \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.
+% \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.
+% \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}.
+% \end{function}
+%
+% \section{Adding entries to property lists}
+%
+% \begin{function}[updated = 2012-07-09]
+% {
+% \prop_put:Nnn, \prop_put:NnV, \prop_put:Nno, \prop_put:Nnx,
+% \prop_put:NVn, \prop_put:NVV, \prop_put:Non, \prop_put:Noo,
+% \prop_put:cnn, \prop_put:cnV, \prop_put:cno, \prop_put:cnx,
+% \prop_put:cVn, \prop_put:cVV, \prop_put:con, \prop_put:coo,
+% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nnx,
+% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:Non, \prop_gput:Noo,
+% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cnx,
+% \prop_gput:cVn, \prop_gput:cVV, \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}. 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. If the \meta{key} is already present
+% in the \meta{property list}, the existing entry is overwritten
+% by the new \meta{value}.
+% \end{function}
+%
+% \begin{function}
+% {
+% \prop_put_if_new:Nnn, \prop_put_if_new:cnn,
+% \prop_gput_if_new:Nnn, \prop_gput_if_new:cnn
+% }
+% \begin{syntax}
+% \cs{prop_put_if_new: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. If the \meta{key} is not present in the
+% \meta{property list} then a new entry is added. 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}
+%
+% \section{Recovering values from property lists}
+%
+% \begin{function}[updated = 2011-08-28]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:coN,
+% }
+% \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:NoN, \prop_pop:cnN, \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:NoN, \prop_gpop:cnN, \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}[added = 2014-07-17, EXP]{\prop_item:Nn, \prop_item:cn}
+% \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}
+% This function is slower than the non-expandable analogue
+% \cs{prop_get:NnN}.
+% 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{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}
+%
+% \section{Modifying property lists}
+%
+% \begin{function}[added = 2012-05-12]
+% {
+% \prop_remove:Nn, \prop_remove:NV, \prop_remove:cn, \prop_remove:cV,
+% \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:cn, \prop_gremove:cV
+% }
+% \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}[updated = 2011-09-15, EXP, pTF]
+% {
+% \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:No,
+% \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:co
+% }
+% \begin{syntax}
+% \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}
+% This function iterates through every key--value pair in the
+% \meta{property list} and is therefore slower than using the
+% non-expandable \cs{prop_get:NnNTF}.
+% \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 cases follow dependent on the presence
+% or absence of a key in a property list. They offer increased readability
+% and performance over separate testing and recovery phases.
+%
+% \begin{function}[updated = 2012-05-19, TF]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:coN
+% }
+% \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:cnN}
+% \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:cnN}
+% \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 to 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}[updated = 2013-01-08, rEXP]
+% {\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.
+% \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}[updated = 2012-06-29, rEXP]{\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}[updated = 2012-06-29, rEXP]{\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 = 2015-08-01]{\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.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-12, updated = 2015-08-01]{\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.
+% \end{function}
+%
+% \section{Scratch property lists}
+%
+% \begin{variable}[added = 2012-06-23]{\l_tmpa_prop, \l_tmpb_prop}
+% Scratch property lists for local assignment. These are never used by
+% the kernel code, and so are safe for use with any \LaTeX3-defined
+% function. However, they may be overwritten by other non-kernel
+% code and so should only be used for short-term storage.
+% \end{variable}
+%
+% \begin{variable}[added = 2012-06-23]{\g_tmpa_prop, \g_tmpb_prop}
+% Scratch property lists for global assignment. These are never used by
+% 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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=prop>
+% \end{macrocode}
+%
+% A property list is a macro whose top-level expansion is of the form
+% \begin{quote}
+% \cs{s_@@}
+% \cs{@@_pair:wn} \meta{key_1} \cs{s_@@} \Arg{value_1} \\
+% \ldots{} \\
+% \cs{@@_pair:wn} \meta{key_n} \cs{s_@@} \Arg{value_n} \\
+% \end{quote}
+% where \cs{s_@@} is a scan mark
+% (equal to \cs{scan_stop:}), and \cs{@@_pair:wn} can be used to map
+% through the property list.
+%
+% \begin{variable}{\s_@@}
+% The internal token used at the beginning of property lists. This is
+% also used after each \meta{key} (see \cs{@@_pair:wn}).
+% \end{variable}
+%
+% \begin{variable}{\@@_pair:wn}
+% \begin{syntax}
+% \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{item}
+% \end{syntax}
+% The internal token used to begin each key--value pair in the
+% property list. If expanded outside of a mapping or manipulation
+% function, an error is raised. The definition should always be
+% set globally.
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Token list used to store new key--value pairs to be inserted by
+% functions of the \cs{prop_put:Nnn} family.
+% \end{variable}
+%
+% \begin{function}[updated = 2013-01-08]{\@@_split:NnTF}
+% \begin{syntax}
+% \cs{@@_split:NnTF} \meta{property list} \Arg{key} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Splits the \meta{property list} at the \meta{key}, giving three
+% token lists: the \meta{extract} of \meta{property list} before the
+% \meta{key}, the \meta{value} associated with the \meta{key} and the
+% \meta{extract} of the \meta{property list} after the \meta{value}.
+% Both \meta{extracts} retain the internal structure of a property
+% list, and the concatenation of the two \meta{extracts} is a
+% property list.
+% If the \meta{key} is present in the \meta{property list} then the
+% \meta{true code} is left in the input stream, with |#1|, |#2|, and
+% |#3| replaced by the first \meta{extract}, the \meta{value}, and the
+% second extract.
+% If the \meta{key} is not present in the \meta{property list} then
+% the \meta{false code} is left in the input stream, with no trailing
+% material.
+% Both \meta{true code} and \meta{false code} are used in the
+% replacement text of a macro defined internally, hence macro
+% parameter characters should be doubled, except |#1|, |#2|, and |#3|
+% which stand in the \meta{true code} for the three extracts from the
+% property list.
+% The \meta{key} comparison takes place as described for \cs{str_if_eq:nn}.
+% \end{function}
+%
+% \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}{\@@_pair:wn}
+% The delimiter is always defined, but when misused simply triggers an
+% error and removes its argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_pair:wn #1 \s_@@ #2
+ { \__kernel_msg_expandable_error:nn { kernel } { misused-prop } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% Token list used to store the new key--value pair inserted by
+% \cs{prop_put:Nnn} and friends.
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}[tested = m3prop004]{\c_empty_prop}
+% An empty prop.
+% \begin{macrocode}
+\tl_const:Nn \c_empty_prop { \s_@@ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Allocation and initialisation}
+%
+% \begin{macro}[tested = m3prop001]{\prop_new:N, \prop_new:c}
+% 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_clear:N, \prop_clear:c}
+% \begin{macro}[tested = m3prop001]{\prop_gclear:N, \prop_gclear:c}
+% The same idea for clearing.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_clear:N #1
+ { \prop_set_eq:NN #1 \c_empty_prop }
+\cs_generate_variant:Nn \prop_clear:N { c }
+\cs_new_protected:Npn \prop_gclear:N #1
+ { \prop_gset_eq:NN #1 \c_empty_prop }
+\cs_generate_variant:Nn \prop_gclear:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]{\prop_clear_new:N, \prop_clear_new:c}
+% \begin{macro}[tested = m3prop001]{\prop_gclear_new:N, \prop_gclear_new:c}
+% Once again a simple variation of the token list functions.
+% \begin{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 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop001]
+% {\prop_set_eq:NN, \prop_set_eq:cN, \prop_set_eq:Nc, \prop_set_eq:cc}
+% \begin{macro}[tested = m3prop001]
+% {\prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc}
+% These are simply copies from the token list functions.
+% \begin{macrocode}
+\cs_new_eq:NN \prop_set_eq:NN \tl_set_eq:NN
+\cs_new_eq:NN \prop_set_eq:Nc \tl_set_eq:Nc
+\cs_new_eq:NN \prop_set_eq:cN \tl_set_eq:cN
+\cs_new_eq:NN \prop_set_eq:cc \tl_set_eq:cc
+\cs_new_eq:NN \prop_gset_eq:NN \tl_gset_eq:NN
+\cs_new_eq:NN \prop_gset_eq:Nc \tl_gset_eq:Nc
+\cs_new_eq:NN \prop_gset_eq:cN \tl_gset_eq:cN
+\cs_new_eq:NN \prop_gset_eq:cc \tl_gset_eq:cc
+% \end{macrocode}
+% \end{macro}
+% \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{variable}{\l_@@_internal_prop}
+% Property list used by \cs{prop_set_from_keyval:Nn} and others.
+% \begin{macrocode}
+\prop_new:N \l_@@_internal_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\prop_set_from_keyval:Nn, \prop_set_from_keyval:cn}
+% \begin{macro}{\prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn}
+% \begin{macro}{\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
+% \begin{macro}
+% {
+% \@@_from_keyval:n,
+% \@@_from_keyval_loop:w,
+% \@@_from_keyval_split:Nw,
+% \@@_from_keyval_key:n,
+% \@@_from_keyval_key:w,
+% \@@_from_keyval_value:n,
+% \@@_from_keyval_value:w
+% }
+% To avoid tracking throughout the loop the variable name and whether
+% the assignment is local/global, do everything in a scratch variable
+% and empty it afterwards to avoid wasting memory. Loop through items
+% separated by commas, with \cs{prg_do_nothing:} to avoid losing
+% braces. After checking for termination, split the item at the first
+% and then at the second |=| (which ought to be the first of the
+% trailing~|=| that we added). For both splits trim spaces and call a
+% function (first \cs{@@_from_keyval_key:w} then
+% \cs{@@_from_keyval_value:w}), followed by the trimmed material,
+% \cs{q_nil}, the subsequent part of the item, and the trailing |=|'s
+% and \cs{q_stop}. After finding the \meta{key} just store it after
+% \cs{q_stop}. After finding the \meta{value} ignore completely empty
+% items (both trailing~|=| were used as delimiters and all parts are
+% empty); if the remaining part~|#2| consists exactly of the second
+% trailing~|=| (namely there was exactly one |=|~in the item) then
+% output one key--value pair for the property list; otherwise complain
+% about a missing or extra~|=|.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_set_from_keyval:Nn #1#2
+ {
+ \prop_clear:N \l_@@_internal_prop
+ \@@_from_keyval:n {#2}
+ \prop_set_eq:NN #1 \l_@@_internal_prop
+ \prop_clear:N \l_@@_internal_prop
+ }
+\cs_generate_variant:Nn \prop_set_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_gset_from_keyval:Nn #1#2
+ {
+ \prop_clear:N \l_@@_internal_prop
+ \@@_from_keyval:n {#2}
+ \prop_gset_eq:NN #1 \l_@@_internal_prop
+ \prop_clear:N \l_@@_internal_prop
+ }
+\cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c }
+\cs_new_protected:Npn \prop_const_from_keyval:Nn #1#2
+ {
+ \prop_clear:N \l_@@_internal_prop
+ \@@_from_keyval:n {#2}
+ \tl_const:Nx #1 { \exp_not:o \l_@@_internal_prop }
+ \prop_clear:N \l_@@_internal_prop
+ }
+\cs_generate_variant:Nn \prop_const_from_keyval:Nn { c }
+\cs_new_protected:Npn \@@_from_keyval:n #1
+ {
+ \@@_from_keyval_loop:w \prg_do_nothing: #1 ,
+ \q_recursion_tail , \q_recursion_stop
+ }
+\cs_new_protected:Npn \@@_from_keyval_loop:w #1 ,
+ {
+ \quark_if_recursion_tail_stop:o {#1}
+ \@@_from_keyval_split:Nw \@@_from_keyval_key:n
+ #1 = = \q_stop {#1}
+ \@@_from_keyval_loop:w \prg_do_nothing:
+ }
+\cs_new_protected:Npn \@@_from_keyval_split:Nw #1#2 =
+ { \tl_trim_spaces_apply:oN {#2} #1 }
+\cs_new_protected:Npn \@@_from_keyval_key:n #1
+ { \@@_from_keyval_key:w #1 \q_nil }
+\cs_new_protected:Npn \@@_from_keyval_key:w #1 \q_nil #2 \q_stop
+ {
+ \@@_from_keyval_split:Nw \@@_from_keyval_value:n
+ \prg_do_nothing: #2 \q_stop {#1}
+ }
+\cs_new_protected:Npn \@@_from_keyval_value:n #1
+ { \@@_from_keyval_value:w #1 \q_nil }
+\cs_new_protected:Npn \@@_from_keyval_value:w #1 \q_nil #2 \q_stop #3#4
+ {
+ \tl_if_empty:nF { #3 #1 #2 }
+ {
+ \str_if_eq:nnTF {#2} { = }
+ { \prop_put:Nnn \l_@@_internal_prop {#3} {#1} }
+ {
+ \__kernel_msg_error:nnx { kernel } { prop-keyval }
+ { \exp_not:o {#4} }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Accessing data in property lists}
+%
+% \begin{macro}{\@@_split:NnTF}
+% \begin{macro}{\@@_split_aux:NnTF}
+% \begin{macro}[EXP]{\@@_split_aux:w}
+% This function is used by most of the module, and hence must be fast.
+% It receives a \meta{property list}, a \meta{key}, a \meta{true code}
+% and a \meta{false code}. The aim is to split the \meta{property
+% list} at the given \meta{key} into the \meta{extract_1} before the
+% key--value pair, the \meta{value} associated with the \meta{key} and
+% the \meta{extract_2} after the key--value pair. This is done using
+% a delimited function, whose definition is as follows, where the
+% \meta{key} is turned into a string.
+% \begin{quote}
+% \cs{cs_set:Npn} \cs{@@_split_aux:w} |#1| \\
+% \quad \cs{@@_pair:wn} \meta{key} \cs{s_@@} |#2| \\
+% \quad |#3| \cs{q_mark} |#4| |#5| \cs{q_stop} \\
+% \quad |{| |#4| \Arg{true code} \Arg{false code} |}|
+% \end{quote}
+%
+% If the \meta{key} is present in the property list,
+% \cs{@@_split_aux:w}'s |#1| is the part before the \meta{key}, |#2|
+% is the \meta{value}, |#3| is the part after the \meta{key}, |#4| is
+% \cs{use_i:nn}, and |#5| is additional tokens that we do not care
+% about. The \meta{true code} is left in the input stream, and can
+% use the parameters |#1|, |#2|, |#3| for the three parts of the
+% property list as desired. Namely, the original property list is in
+% this case |#1| \cs{@@_pair:wn} \meta{key} \cs{s_@@} |{#2}| |#3|.
+%
+% If the \meta{key} is not there, then the \meta{function} is
+% \cs{use_ii:nn}, which keeps the \meta{false code}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_split:NnTF #1#2
+ { \exp_args:NNo \@@_split_aux:NnTF #1 { \tl_to_str:n {#2} } }
+\cs_new_protected:Npn \@@_split_aux:NnTF #1#2#3#4
+ {
+ \cs_set:Npn \@@_split_aux:w ##1
+ \@@_pair:wn #2 \s_@@ ##2 ##3 \q_mark ##4 ##5 \q_stop
+ { ##4 {#3} {#4} }
+ \exp_after:wN \@@_split_aux:w #1 \q_mark \use_i:nn
+ \@@_pair:wn #2 \s_@@ { } \q_mark \use_ii:nn \q_stop
+ }
+\cs_new:Npn \@@_split_aux:w { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {\prop_remove:Nn, \prop_remove:NV, \prop_remove:cn, \prop_remove:cV}
+% \begin{macro}[tested = m3prop002]
+% {\prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:cn, \prop_gremove:cV}
+% Deleting from a property starts by splitting the list.
+% If the key is present in the property list, the returned value is ignored.
+% If the key is missing, nothing happens.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_remove:Nn #1#2
+ {
+ \@@_split:NnTF #1 {#2}
+ { \tl_set:Nn #1 { ##1 ##3 } }
+ { }
+ }
+\cs_new_protected:Npn \prop_gremove:Nn #1#2
+ {
+ \@@_split:NnTF #1 {#2}
+ { \tl_gset:Nn #1 { ##1 ##3 } }
+ { }
+ }
+\cs_generate_variant:Nn \prop_remove:Nn { NV }
+\cs_generate_variant:Nn \prop_remove:Nn { c , cV }
+\cs_generate_variant:Nn \prop_gremove:Nn { NV }
+\cs_generate_variant:Nn \prop_gremove:Nn { c , cV }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:coN
+% }
+% Getting an item from a list is very easy: after splitting,
+% if the key is in the property list, just set the token list variable
+% to the return value, otherwise to \cs{q_no_value}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_get:NnN #1#2#3
+ {
+ \@@_split:NnTF #1 {#2}
+ { \tl_set:Nn #3 {##2} }
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_get:NnN { NV , No }
+\cs_generate_variant:Nn \prop_get:NnN { c , cV , co }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {\prop_pop:NnN, \prop_pop:NoN, \prop_pop:cnN, \prop_pop:coN}
+% \begin{macro}[tested = m3prop002]
+% {\prop_gpop:NnN, \prop_gpop:NoN, \prop_gpop:cnN, \prop_gpop:coN}
+% Popping a value also starts by doing the split.
+% If the key is present, save the value in the token list and update the
+% property list as when deleting.
+% If the key is missing, save \cs{q_no_value} in the token list.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_pop:NnN #1#2#3
+ {
+ \@@_split:NnTF #1 {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \tl_set:Nn #1 { ##1 ##3 }
+ }
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_new_protected:Npn \prop_gpop:NnN #1#2#3
+ {
+ \@@_split:NnTF #1 {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \tl_gset:Nn #1 { ##1 ##3 }
+ }
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\cs_generate_variant:Nn \prop_pop:NnN { No }
+\cs_generate_variant:Nn \prop_pop:NnN { c , co }
+\cs_generate_variant:Nn \prop_gpop:NnN { No }
+\cs_generate_variant:Nn \prop_gpop:NnN { c , co }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\prop_item:Nn, \prop_item:cn}
+% \begin{macro}[EXP]{\@@_item_Nn:nwwn}
+% Getting the value corresponding to a key in a property list in an
+% expandable fashion is similar to mapping some tokens. Go through
+% the property list one \meta{key}--\meta{value} pair at a time: the
+% arguments of \cs{@@_item_Nn:nwn} are the \meta{key} we are looking
+% for, a \meta{key} of the property list, and its associated value.
+% The \meta{keys} are compared (as strings). If they match, the
+% \meta{value} is returned, within \cs{exp_not:n}. The loop
+% terminates even if the \meta{key} is missing, and yields an empty
+% value, because we have appended the appropriate
+% \meta{key}--\meta{empty value} pair to the property list.
+% \begin{macrocode}
+\cs_new:Npn \prop_item:Nn #1#2
+ {
+ \exp_last_unbraced:Noo \@@_item_Nn:nwwn { \tl_to_str:n {#2} } #1
+ \@@_pair:wn \tl_to_str:n {#2} \s_@@ { }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_item_Nn:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4
+ {
+ \str_if_eq:eeTF {#1} {#3}
+ { \prg_break:n { \exp_not:n {#4} } }
+ { \@@_item_Nn:nwwn {#1} }
+ }
+\cs_generate_variant:Nn \prop_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\prop_count:N, \prop_count:c}
+% \begin{macro}[EXP]{\@@_count:nn}
+% Counting the key--value pairs in a property list is done using the
+% same approach as for other count functions: turn each entry into a
+% \texttt{+1} then use integer evaluation to actually do the
+% mathematics.
+% \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}[TF, tested = m3prop004]
+% {\prop_pop:NnN, \prop_pop:cnN, \prop_gpop:NnN, \prop_gpop:cnN}
+% Popping an item from a property list, keeping track of whether
+% the key was present or not, is implemented as a conditional.
+% If the key was missing, neither the property list, nor the token
+% list are altered. Otherwise, \cs{prg_return_true:} is used after
+% the assignments.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \prop_pop:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_split:NnTF #1 {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \tl_set:Nn #1 { ##1 ##3 }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_new_protected_conditional:Npnn \prop_gpop:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_split:NnTF #1 {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \tl_gset:Nn #1 { ##1 ##3 }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \prop_pop:NnN { c } { T , F , TF }
+\prg_generate_conditional_variant:Nnn \prop_gpop:NnN { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_put:Nnn, \prop_put:NnV, \prop_put:Nno, \prop_put:Nnx,
+% \prop_put:NVn, \prop_put:NVV, \prop_put:Non, \prop_put:Noo,
+% \prop_put:cnn, \prop_put:cnV, \prop_put:cno, \prop_put:cnx,
+% \prop_put:cVn, \prop_put:cVV, \prop_put:con, \prop_put:coo
+% }
+% \begin{macro}[tested = m3prop002]
+% {
+% \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nno, \prop_gput:Nnx,
+% \prop_gput:NVn, \prop_gput:NVV, \prop_gput:Non, \prop_gput:Noo,
+% \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cno, \prop_gput:cnx,
+% \prop_gput:cVn, \prop_gput:cVV, \prop_gput:con, \prop_gput:coo
+% }
+% \begin{macro}{\@@_put:NNnn}
+% Since the branches of \cs{@@_split:NnTF} are used as the replacement
+% text of an internal macro, and since the \meta{key} and new
+% \meta{value} may contain arbitrary tokens, it is not safe to include
+% them in the argument of \cs{@@_split:NnTF}. We thus start by
+% storing in \cs{l_@@_internal_tl} tokens which (after
+% \texttt{x}-expansion) encode the key--value pair. This variable can
+% safely be used in \cs{@@_split:NnTF}. If the \meta{key} was absent,
+% append the new key--value to the list.
+% Otherwise concatenate the extracts |##1|
+% and |##3| with the new key--value pair \cs{l_@@_internal_tl}. The
+% updated entry is placed at the same spot as the original \meta{key}
+% in the property list, preserving the order of entries.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_put:Nnn { \@@_put:NNnn \tl_set:Nx }
+\cs_new_protected:Npn \prop_gput:Nnn { \@@_put:NNnn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_put:NNnn #1#2#3#4
+ {
+ \tl_set:Nn \l_@@_internal_tl
+ {
+ \exp_not:N \@@_pair:wn \tl_to_str:n {#3}
+ \s_@@ { \exp_not:n {#4} }
+ }
+ \@@_split:NnTF #2 {#3}
+ { #1 #2 { \exp_not:n {##1} \l_@@_internal_tl \exp_not:n {##3} } }
+ { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } }
+ }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { NnV , Nno , Nnx , NV , NVV , No , Noo }
+\cs_generate_variant:Nn \prop_put:Nnn
+ { c , cnV , cno , cnx , cV , cVV , co , coo }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { NnV , Nno , Nnx , NV , NVV , No , Noo }
+\cs_generate_variant:Nn \prop_gput:Nnn
+ { c , cnV , cno , cnx , cV , cVV , co , coo }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[tested = m3prop002]
+% {\prop_put_if_new:Nnn, \prop_put_if_new:cnn}
+% \begin{macro}[tested = m3prop002]
+% {\prop_gput_if_new:Nnn, \prop_gput_if_new:cnn}
+% \begin{macro}{\@@_put_if_new:NNnn}
+% Adding conditionally also splits. If the key is already present,
+% the three brace groups given by \cs{@@_split:NnTF} are removed.
+% If the key is new, then the value is added, being careful to
+% convert the key to a string using \cs{tl_to_str:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_put_if_new:Nnn
+ { \@@_put_if_new:NNnn \tl_set:Nx }
+\cs_new_protected:Npn \prop_gput_if_new:Nnn
+ { \@@_put_if_new:NNnn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_put_if_new:NNnn #1#2#3#4
+ {
+ \tl_set:Nn \l_@@_internal_tl
+ {
+ \exp_not:N \@@_pair:wn \tl_to_str:n {#3}
+ \s_@@ \exp_not:n { {#4} }
+ }
+ \@@_split:NnTF #2 {#3}
+ { }
+ { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } }
+ }
+\cs_generate_variant:Nn \prop_put_if_new:Nnn { c }
+\cs_generate_variant:Nn \prop_gput_if_new:Nnn { c }
+% \end{macrocode}
+% \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}
+% Same test as for token lists.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \prop_if_empty:N #1 { p , T , F , TF }
+ {
+ \tl_if_eq:NNTF #1 \c_empty_prop
+ \prg_return_true: \prg_return_false:
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_empty:N
+ { c } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, tested = m3prop003]
+% {
+% \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:No,
+% \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:co
+% }
+% \begin{macro}[EXP]{\@@_if_in:nwwn, \@@_if_in:N}
+% Testing expandably if a key is in a property list
+% requires to go through the key--value pairs one by one.
+% This is rather slow, and a faster test would be
+% \begin{verbatim}
+% \prg_new_protected_conditional:Npnn \prop_if_in:Nn #1 #2
+% {
+% \@@_split:NnTF #1 {#2}
+% { \prg_return_true: }
+% { \prg_return_false: }
+% }
+% \end{verbatim}
+% but \cs{@@_split:NnTF} is non-expandable.
+%
+% Instead, the key is compared to each key in turn using
+% \cs{str_if_eq:ee}, which is expandable. To terminate the mapping,
+% we append to the property list the key that is searched for. This
+% second \cs{tl_to_str:n} is not expanded at the start, but only when
+% included in the \cs{str_if_eq:ee}. It cannot make the breaking
+% mechanism choke, because the arbitrary token list material is
+% enclosed in braces. The second argument of \cs{@@_if_in:nwwn} is
+% most often empty. When the \meta{key} is found in the list,
+% \cs{@@_if_in:N} receives \cs{@@_pair:wn}, and if it is
+% found as the extra item, the function receives
+% \cs{q_recursion_tail}, easily recognizable.
+%
+% Here, \cs{prop_map_function:NN} is not sufficient for the mapping,
+% since it can only map a single token, and cannot carry the key that
+% is searched for.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF }
+ {
+ \exp_last_unbraced:Noo \@@_if_in:nwwn { \tl_to_str:n {#2} } #1
+ \@@_pair:wn \tl_to_str:n {#2} \s_@@ { }
+ \q_recursion_tail
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_if_in:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4
+ {
+ \str_if_eq:eeTF {#1} {#3}
+ { \@@_if_in:N }
+ { \@@_if_in:nwwn {#1} }
+ }
+\cs_new:Npn \@@_if_in:N #1
+ {
+ \if_meaning:w \q_recursion_tail #1
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \prg_break:
+ }
+\prg_generate_conditional_variant:Nnn \prop_if_in:Nn
+ { NV , No , c , cV , co } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Recovering values from property lists with branching}
+%
+% \begin{macro}[TF, tested = m3prop004]
+% {
+% \prop_get:NnN, \prop_get:NVN, \prop_get:NoN,
+% \prop_get:cnN, \prop_get:cVN, \prop_get:coN
+% }
+% Getting the value corresponding to a key, keeping track of whether
+% the key was present or not, is implemented as a conditional (with
+% side effects). If the key was absent, the token list is not altered.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF }
+ {
+ \@@_split:NnTF #1 {#2}
+ {
+ \tl_set:Nn #3 {##2}
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+\prg_generate_conditional_variant:Nnn \prop_get:NnN
+ { NV , No , c , cV , co } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Mapping to 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:Nwwn}
+% The argument delimited by \cs{@@_pair:wn} is empty except at the end
+% of the loop where it is \cs{prg_break:}. No need for any quark
+% test.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_function:NN #1#2
+ {
+ \exp_after:wN \use_i_ii:nnn
+ \exp_after:wN \@@_map_function:Nwwn
+ \exp_after:wN #2
+ #1
+ \prg_break: \@@_pair:wn \s_@@ { } \prg_break_point:
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_function:Nwwn #1#2 \@@_pair:wn #3 \s_@@ #4
+ {
+ #2
+ #1 {#3} {#4}
+ \@@_map_function:Nwwn #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}
+ #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}[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{Viewing property lists}
+%
+% \begin{macro}[tested = m3show001]
+% {\prop_show:N, \prop_show:c, \prop_log:N, \prop_log:c}
+% Apply the general \cs{__kernel_chk_defined:NT} and
+% \cs{msg_show:nnnnnn}. Contrarily to sequences and comma lists,
+% we use \cs{msg_show_item:nn} to format both the key and the value
+% for each pair.
+% \begin{macrocode}
+\cs_new_protected:Npn \prop_show:N { \@@_show:NN \msg_show:nnxxxx }
+\cs_generate_variant:Nn \prop_show:N { c }
+\cs_new_protected:Npn \prop_log:N { \@@_show:NN \msg_log:nnxxxx }
+\cs_generate_variant:Nn \prop_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { LaTeX/kernel } { show-prop }
+ { \token_to_str:N #2 }
+ { \prop_map_function:NN #2 \msg_show_item:nn }
+ { } { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3quark.dtx b/macros/latex/contrib/l3kernel/l3quark.dtx
new file mode 100644
index 0000000000..9ef207f346
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3quark.dtx
@@ -0,0 +1,636 @@
+% \iffalse meta-comment
+%
+%% File: l3quark.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Quarks^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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 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.
+% An example of the quark testing functions and their use in recursion
+% can be seen in the implementation of \cs{clist_map_function:NN}.
+%
+% \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}{\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}[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}{\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}[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}[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}
+%
+% \section{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}[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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \subsection{Quarks}
+%
+% \begin{macrocode}
+%<@@=quark>
+% \end{macrocode}
+%
+% \begin{macro}{\quark_new:N}
+% \UnitTested
+% Allocate a new quark.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN q #1 } { }
+\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{macro}{\quark_if_recursion_tail_stop:N}
+% \UnitTested
+% \begin{macro}{\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}
+% {\quark_if_recursion_tail_stop:n, \quark_if_recursion_tail_stop:o}
+% \UnitTested
+% \begin{macro}
+% {\quark_if_recursion_tail_stop_do:nn, \quark_if_recursion_tail_stop_do:on}
+% \UnitTested
+% \begin{macro}{\@@_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}{\quark_if_recursion_tail_break:NN}
+% \begin{macro}{\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 |\quark_if_nil:n(TF)|. 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}
+%
+% \subsection{Scan marks}
+%
+% \begin{macrocode}
+%<@@=scan>
+% \end{macrocode}
+%
+% \begin{variable}{\g_@@_marks_tl}
+% \UnitTested
+% The list of all scan marks currently declared.
+% \begin{macrocode}
+\tl_new:N \g_@@_marks_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN s #1 } { }
+\cs_new_protected:Npn \scan_new:N #1
+ {
+ \tl_if_in:NnTF \g_@@_marks_tl { #1 }
+ {
+ \__kernel_msg_error:nnx { kernel } { 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}
+% \UnitTested
+% We only declare one scan mark here, more can be defined
+% by specific modules.
+% \begin{macrocode}
+\scan_new:N \s_stop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3regex.dtx b/macros/latex/contrib/l3kernel/l3regex.dtx
new file mode 100644
index 0000000000..f4f3fb1ddd
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3regex.dtx
@@ -0,0 +1,6807 @@
+% \iffalse meta-comment
+%
+%% File: l3regex.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3regex} package: Regular expressions in \TeX{}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+% \newenvironment{l3regex-syntax}
+% {\begin{itemize}\def\\{\char`\\}\def\makelabel##1{\hss\llap{\ttfamily##1}}}
+% {\end{itemize}}
+%
+% The \pkg{l3regex} package 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{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_const:Nn}. For example,
+% \begin{verbatim}
+% \regex_const:Nn \c_foo_regex { \c{begin} \cB. (\c[^BE].*) \cE. }
+% \end{verbatim}
+% stores in \cs{c_foo_regex} a regular expression which matches the
+% starting marker for an environment: \cs{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}
+%
+% 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.
+%
+% 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}
+%
+% Character types.
+% \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|).
+%
+% 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}
+%
+% 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}
+%
+% Alternation and capturing groups.
+% \begin{l3regex-syntax}
+% \item[A\char`|B\char`|C] Either one of \texttt{A}, \texttt{B},
+% or \texttt{C}.
+% \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}
+%
+% 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{begin}, and nothing else.
+% \item[\\cX] Applies to the next object, which can be a character,
+% character property, 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.
+% \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{tl~var~name} matches
+% the exact contents of the token list \meta{tl~var}. 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
+% not supported directly: use a group.
+%
+% The option |(?i)| makes the match case insensitive (identifying
+% \texttt{A}--\texttt{Z} with \texttt{a}--\texttt{z}; no Unicode support
+% yet). 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)[Y-\\]| is equivalent to |[YZ\[\\yz]|, and
+% |(?i)[^aeiou]| matches any character which is not a vowel. Neither
+% character properties, nor |\c{...}| nor |\u{...}| are affected by the
+% |i| option.
+% ^^A \]
+%
+% 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.
+%
+% 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{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{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.
+%
+% \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{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.
+%
+% The characters inserted by the replacement have category code $12$
+% (other) by default, with the exception of space characters. Spaces
+% inserted through \verb*|\ | have category code $10$, while spaces
+% inserted through |\x20| or |\x{20}| have category code $12$.
+% 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{tl~var~name} allows to insert the
+% contents of the token list with name \meta{tl~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{l_my_tl} holding |first,\emph{second},first,first|.
+%
+% \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, \regex_const: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}. 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}
+% The assignment is local for \cs{regex_set:Nn} and global for
+% \cs{regex_gset:Nn}. Use \cs{regex_const:Nn} for compiled expressions
+% which never change.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-26]{\regex_show:n, \regex_show:N}
+% \begin{syntax}
+% \cs{regex_show:n} \Arg{regex}
+% \end{syntax}
+% Shows 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
+% +-branch
+% char code 89
+% \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_(g)set:Nn}.
+%
+% \begin{function}[TF, added = 2017-05-26]{\regex_match:nn, \regex_match:Nn}
+% \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:NnN}
+% \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{l_foo_int} taking the value $5$.
+% \end{function}
+%
+% \section{Submatch extraction}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {\regex_extract_once:nnN, \regex_extract_once:NnN}
+% \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, |\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 |\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:NnN}
+% \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:NnN}
+% \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:NnN}
+% \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 \meta{token list}
+% and replaces the first match with the \meta{replacement}. The result
+% is assigned locally to \meta{tl~var}. In the \meta{replacement},
+% |\0| represents the full match, |\1| represent the contents of the
+% first capturing group, |\2| of the second, \emph{etc.}
+% \end{function}
+%
+% \begin{function}[noTF, added = 2017-05-26]
+% {\regex_replace_all:nnN, \regex_replace_all:NnN}
+% \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 \cs{regular expression} in the
+% \meta{token list} 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}
+%
+% \section{Constants and variables}
+%
+% \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{c_max_register_int}.
+% \item Find out whether the fact that |\W| and friends match the
+% end-marker leads to bugs. Possibly update \cs{__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{__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{current_state} and \texttt{current_submatches}.
+% \item If possible, when a state is reused by the same thread, kill
+% other subthreads.
+% \item Use an array rather than \cs{l__regex_balance_tl}
+% to build the function \cs{__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{__regex_action_free:n}.
+% \item Optimize the use of \cs{__regex_action_success:} by inserting it
+% in state $2$ directly instead of having an extra transition.
+% \item Optimize the use of \cs{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.
+% \item Instead of checking whether the character is special or
+% alphanumeric using its character code, check if it is special in
+% regexes with \cs{cs_if_exist} tests.
+% \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.
+% \item Provide a syntax such as |\ur{l_my_regex}| to use an
+% already-compiled regex in a more complicated regex. This makes
+% regexes more easily composable.
+% \item Allowing |\u{l_my_tl}| in more places, for instance as the
+% number of repetitions in a quantifier.
+% \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}
+%<*initex|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.
+% \item \emph{Position}: each token in the query is labelled by an
+% integer \meta{position}, with $\texttt{min_pos} - 1 \leq
+% \meta{position} \leq \texttt{max_pos}$. The lowest and highest
+% positions correspond to imaginary begin and end markers (with
+% inaccessible category code and character code).
+% \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 (stored into
+% some dimension registers in scaled points). 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_state_intarray} maps each \meta{thread} (with
+% $\texttt{min_active} \leq \meta{thread} < \texttt{max_active}$) to
+% the \meta{state} in which the \meta{thread} currently is. The
+% \meta{threads} are ordered starting from the best to the least
+% preferred.
+% \item \tn{toks}\meta{thread} holds the submatch information for the
+% \meta{thread}, as the contents of a property list.
+% \item \cs{g_@@_charcode_intarray} and \cs{g_@@_catcode_intarray} hold the
+% character codes and category codes of tokens at each
+% \meta{position} in the query.
+% \item \cs{g_@@_balance_intarray} holds the balance of begin-group and
+% end-group character tokens which appear before that point in the
+% token list.
+% \item \tn{toks}\meta{position} holds \meta{tokens} which \texttt{o}-
+% and \texttt{x}-expand to the \meta{position}-th token in the query.
+% \item \cs{g_@@_submatch_prev_intarray}, \cs{g_@@_submatch_begin_intarray}
+% and \cs{g_@@_submatch_end_intarray} hold, for each submatch (as would
+% be extracted by \cs{regex_extract_all:nnN}), the place where the
+% 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}
+%
+% 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
+ { \@@_toks_set:Nn #1 { } }
+\cs_new_eq:NN \@@_toks_set:Nn \tex_toks:D
+\cs_new_protected:Npn \@@_toks_set:No #1
+ { \@@_toks_set:Nn #1 \exp_after:wN }
+% \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:Nx}
+% \begin{macro}{\@@_toks_put_right:Nx, \@@_toks_put_right:Nn}
+% During the building phase we wish to add \texttt{x}-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:Nx} is provided because it is more
+% efficient than \texttt{x}-expanding with \cs{exp_not:n}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_toks_put_left:Nx #1#2
+ {
+ \cs_set:Npx \@@_tmp:w { #2 }
+ \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN
+ { \exp_after:wN \@@_tmp:w \tex_the:D \tex_toks:D #1 }
+ }
+\cs_new_protected:Npn \@@_toks_put_right:Nx #1#2
+ {
+ \cs_set:Npx \@@_tmp:w {#2}
+ \tex_toks:D #1 \exp_after:wN
+ { \tex_the:D \tex_toks:D \exp_after:wN #1 \@@_tmp:w }
+ }
+\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{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
+ \tex_the:D \tex_toks:D \l_@@_curr_pos_int
+ }
+% \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}{\g_@@_charcode_intarray, \g_@@_catcode_intarray, \g_@@_balance_intarray}
+% The first thing we do when matching is to go once through the query
+% token list and store the information for each token into
+% \cs{g_@@_charcode_intarray}, \cs{g_@@_catcode_intarray} and \tn{toks}
+% registers. We also store the balance of begin-group/end-group
+% characters into \cs{g_@@_balance_intarray}.
+% \begin{macrocode}
+\intarray_new:Nn \g_@@_charcode_intarray { 65536 }
+\intarray_new:Nn \g_@@_catcode_intarray { 65536 }
+\intarray_new:Nn \g_@@_balance_intarray { 65536 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_balance_int}
+% During this phase, \cs{l_@@_balance_int} counts the balance of
+% begin-group and end-group character tokens which appear before a
+% 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}
+%
+% \begin{variable}{\l_@@_cs_name_tl}
+% This variable is used in \cs{@@_item_cs:n} to store the csname of
+% the currently-tested token when the regex contains a sub-regex for
+% testing csnames.
+% \begin{macrocode}
+\tl_new:N \l_@@_cs_name_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Testing characters}
+%
+% \begin{macro}{\c_@@_ascii_min_int, \c_@@_ascii_max_control_int, \c_@@_ascii_max_int}
+% \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}
+%
+% \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{current_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:
+ \if_int_compare:w \l_@@_case_changed_char_int = \c_max_int
+ \@@_compute_case_changed_char:
+ \fi:
+ \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:
+ \if_int_compare:w \l_@@_case_changed_char_int = \c_max_int
+ \@@_compute_case_changed_char:
+ \fi:
+ \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 (or rather, when it is set to the marker value
+% \cs{c_max_int}). 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:
+ }
+% \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:
+ }
+\cs_new_protected:Npn \@@_item_catcode:nT #1
+ {
+ \if_int_odd:w \int_eval:n { #1 / \@@_item_catcode: } \exp_stop_f:
+ \exp_after:wN \use:n
+ \else:
+ \exp_after:wN \use_none:n
+ \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.
+% \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 = 0
+ {
+ \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. The three \cs{exp_after:wN} expand the contents
+% of the \tn{toks}\meta{current position} (of the form \cs{exp_not:n}
+% \Arg{control sequence}) to \meta{control sequence}.
+% We store the cs name before building states for the cs, as those
+% states may overlap with toks registers storing the user's input.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_item_cs:n #1
+ {
+ \int_compare:nNnT \l_@@_curr_catcode_int = 0
+ {
+ \group_begin:
+ \tl_set:Nx \l_@@_cs_name_tl { \@@_curr_cs_to_str: }
+ \@@_single_match:
+ \@@_disable_submatches:
+ \@@_build_for_cs:n {#1}
+ \bool_set_eq:NN \l_@@_saved_success_bool
+ \g_@@_success_bool
+ \exp_args:NV \@@_match_cs:n \l_@@_cs_name_tl
+ \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{x}-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{x}-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}
+\__kernel_patch:nnNNpn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_escape_use:nnnn
+ \group_begin:
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_escape_use:nnnn }
+ \use_none:nnn
+ }
+ { }
+\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:
+ \tl_gset:Nx \g_@@_internal_tl
+ { \__kernel_str_to_other_fast:n {#4} }
+ \tl_put_right:Nx \l_@@_internal_a_tl
+ {
+ \exp_after:wN \@@_escape_loop:N \g_@@_internal_tl
+ { break } \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_break:w, \@@_escape_/break: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:NN \@@_escape_break:w \prg_break:
+\cs_new:cpn { @@_escape_/break:w }
+ {
+ \__kernel_msg_expandable_error:nn { kernel } { trailing-backslash }
+ \prg_break:
+ }
+\cs_new:cpn { @@_escape_~:w } { }
+\cs_new:cpx { @@_escape_/a:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^G }
+\cs_new:cpx { @@_escape_/t:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^I }
+\cs_new:cpx { @@_escape_/n:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^J }
+\cs_new:cpx { @@_escape_/f:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^L }
+\cs_new:cpx { @@_escape_/r:w }
+ { \exp_not:N \@@_escape_raw:N \iow_char:N \^^M }
+\cs_new:cpx { @@_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
+ {
+ \__kernel_msg_expandable_error:nnff { kernel } { 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
+ {
+ \str_if_eq:nnTF {#1} { break } { ; }
+ {
+ \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
+ {
+ \str_if_eq:nnTF {#1} { break } { ; }
+ {
+ \@@_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
+ {
+ \str_if_eq:nnTF {#1} { break }
+ { ; \@@_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
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { 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}
+\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}
+%
+% \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_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
+ \prg_return_true: \else: \prg_return_false: \fi:
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \prg_return_true: \else: \prg_return_false: \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ \if_int_compare:w `#1 < `\ \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ \else: \prg_return_false: \fi:
+ \fi:
+ \fi:
+ }
+\prg_new_conditional:Npnn \@@_char_if_alphanumeric:N #1 { TF }
+ {
+ \if_int_compare:w `#1 > `Z \exp_stop_f:
+ \if_int_compare:w `#1 > `z \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 < `a \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ \fi:
+ \else:
+ \if_int_compare:w `#1 > `9 \exp_stop_f:
+ \if_int_compare:w `#1 < `A \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ \else:
+ \if_int_compare:w `#1 < `0 \exp_stop_f:
+ \prg_return_false: \else: \prg_return_true: \fi:
+ \fi:
+ \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{@@_anchor:N} \meta{integer}|}|
+% \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}
+\prg_new_conditional:Npnn \@@_two_if_eq:NNNN #1#2#3#4 { TF }
+ {
+ \if_meaning:w #1 #3
+ \if:w #2 #4
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \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}
+\prg_new_conditional:Npnn \@@_if_raw_digit:NN #1#2 { TF }
+ {
+ \if_meaning:w \@@_compile_raw:N #1
+ \if_int_compare:w 1 < 1 #2 \exp_stop_f:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \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}
+\cs_new:Npn \@@_if_in_class:TF
+ {
+ \if_int_odd:w \l_@@_mode_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \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
+ \exp_after:wN \use_ii:nn
+ \else:
+ \if_int_compare:w \l_@@_mode_int < \c_@@_outer_mode_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
+ }
+% \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
+ \exp_after:wN \use_i:nn
+ \else:
+ \if_int_compare:w \l_@@_mode_int > \c_@@_outer_mode_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_ii:nn
+ \fi:
+ \fi:
+ }
+% \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}
+\cs_new:Npn \@@_if_within_catcode:TF
+ {
+ \if_int_compare:w \l_@@_mode_int > \c_@@_outer_mode_int
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \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
+ \exp_after:wN \use:n
+ \else:
+ \if_int_compare:w \l_@@_mode_int = \c_@@_class_mode_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \use:n
+ \else:
+ \__kernel_msg_error:nn { kernel } { c-bad-mode }
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_none:n
+ \fi:
+ \fi:
+ }
+% \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{x}-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
+ {
+ \__kernel_msg_error:nn { kernel } { missing-rbrack }
+ \use:c { @@_compile_]: }
+ \prg_do_nothing: \prg_do_nothing:
+ }
+ { }
+ \if_int_compare:w \l_@@_group_level_int > 0 \exp_stop_f:
+ \__kernel_msg_error:nnx { kernel } { 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:NNNx
+ \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
+ { \__kernel_msg_error:nn { kernel } { c-trailing } }
+ \int_compare:nNnT \l_@@_mode_int < \c_@@_outer_mode_int
+ {
+ \__kernel_msg_error:nn { kernel } { 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_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:Nx \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:x}
+% 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:x
+ {
+ \exp_args:No \tl_map_function:nN { \tl_to_str:n {#1} }
+ \@@_compile_raw:N
+ }
+ }
+\cs_generate_variant:Nn \@@_compile_abort_tokens:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Quantifiers}
+%
+% \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:xNN}
+% 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:xNN #1#2#3
+ {
+ \@@_compile_quantifier_none:
+ \__kernel_msg_warning:nnxx { kernel } { invalid-quantifier } {#1} {#3}
+ \@@_compile_abort_tokens:x {#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:xNN { \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:xNN
+ { \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:xNN
+ { \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
+ \__kernel_msg_error:nnxx { kernel } { 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:xNN
+ {
+ \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
+ {
+ \__kernel_msg_error:nnx { kernel } { 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}
+\prg_new_protected_conditional:Npnn \@@_if_end_range:NN #1#2 { TF }
+ {
+ \if_meaning:w \@@_compile_raw:N #1
+ \prg_return_true:
+ \else:
+ \if_meaning:w \@@_compile_special:N #1
+ \if_charcode:w ] #2
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ \else:
+ \prg_return_false:
+ \fi:
+ \fi:
+ }
+\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:
+ \__kernel_msg_error:nnxx { kernel } { range-backwards } {#1} {#3}
+ \else:
+ \tl_build_put_right:Nx \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:
+ }
+ {
+ \__kernel_msg_warning:nnxx { kernel } { range-missing-end }
+ {#1} { \c_backslash_str #3 }
+ \tl_build_put_right:Nx \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:cpx { @@_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:cpx { @@_compile_/#1: }
+ { \@@_compile_one:n \exp_not:c { @@_prop_#1: } }
+ \cs_new_protected:cpx { @@_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:NF}
+% \begin{macro}+\@@_compile_^:+
+% \begin{macro}{\@@_compile_/A:, \@@_compile_/G:}
+% \begin{macro}+\@@_compile_$:+
+% \begin{macro}{\@@_compile_/Z:, \@@_compile_/z:}
+% In modes where assertions are allowed, anchor to the start of the
+% query, the start of the match, or the end of the query, depending on
+% the integer |#1|. In other modes, |#2| treats the character as raw,
+% with an error for escaped letters (|$| is valid in a class, but |\A|
+% is definitely a mistake on the user's part).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_compile_anchor:NF #1#2
+ {
+ \@@_if_in_class_or_catcode:TF {#2}
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_assertion:Nn \c_true_bool { \@@_anchor:N #1 } }
+ }
+ }
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:cpn { @@_compile_/#1: }
+ { \@@_compile_anchor:NF #2 { \@@_compile_raw_error:N #1 } }
+ }
+\@@_tmp:w A \l_@@_min_pos_int
+\@@_tmp:w G \l_@@_start_pos_int
+\@@_tmp:w Z \l_@@_max_pos_int
+\@@_tmp:w z \l_@@_max_pos_int
+\cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \cs_new_protected:cpn { @@_compile_#1: }
+ { \@@_compile_anchor:NF #2 { \@@_compile_raw:N #1 } }
+ }
+\exp_args:Nx \@@_tmp:w { \iow_char:N \^ } \l_@@_min_pos_int
+\exp_args:Nx \@@_tmp:w { \iow_char:N \$ } \l_@@_max_pos_int
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_/b:, \@@_compile_/B:}
+% Contrarily to |^| and |$|, which could be implemented without really
+% knowing what precedes in the token list, this requires more
+% information, namely, the knowledge of the last character code.
+% \begin{macrocode}
+\cs_new_protected:cpn { @@_compile_/b: }
+ {
+ \@@_if_in_class_or_catcode:TF
+ { \@@_compile_raw_error:N b }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_assertion:Nn \c_true_bool { \@@_b_test: } }
+ }
+ }
+\cs_new_protected:cpn { @@_compile_/B: }
+ {
+ \@@_if_in_class_or_catcode:TF
+ { \@@_compile_raw_error:N B }
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_assertion:Nn \c_false_bool { \@@_b_test: } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Character classes}
+%
+% \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.
+% \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 }
+ = {
+ \__kernel_msg_warning:nnx { kernel }
+ { posix-unsupported } { = }
+ }
+ . {
+ \__kernel_msg_warning:nnx { kernel }
+ { 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
+ \tl_set:Nx \l_@@_internal_a_tl { \if_false: } \fi:
+ \@@_compile_class_posix_loop:w
+ }
+ {
+ \bool_set_true:N \l_@@_internal_bool
+ \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:NF \l_@@_internal_bool \@@_item_reverse:n
+ \exp_not:c { @@_posix_ \l_@@_internal_a_tl : }
+ }
+ }
+ {
+ \__kernel_msg_warning:nnx { kernel } { posix-unknown }
+ { \l_@@_internal_a_tl }
+ \@@_compile_abort_tokens:x
+ {
+ [: \bool_if:NF \l_@@_internal_bool { ^ }
+ \l_@@_internal_a_tl :]
+ }
+ }
+ }
+ {
+ \__kernel_msg_error:nnxx { kernel } { posix-missing-close }
+ { [: \l_@@_internal_a_tl } { #2 #4 }
+ \@@_compile_abort_tokens:x { [: \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 > 0 \exp_stop_f:
+ \tl_build_put_right:Nn \l_@@_build_tl { \if_false: { \fi: } }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNNx
+ \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:
+ \__kernel_msg_warning:nn { kernel } { 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
+ \__kernel_msg_error:nn { kernel } { 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 }
+ {
+ \__kernel_msg_warning:nnx { kernel } { 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 }
+ }
+ {
+ \__kernel_msg_warning:nnx { kernel } { 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 }
+ }
+ {
+ \__kernel_msg_warning:nnx { kernel } { 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 } }
+ {
+ \__kernel_msg_error:nnx { kernel } { 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 }
+ { \__kernel_msg_error:nnn { kernel } { 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: }
+ }
+ {
+ \__kernel_msg_error:nnx { kernel } { 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:n { \l_@@_catcodes_int / #1 } \exp_stop_f:
+ \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_}:+
+% \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 { @@_cs }
+\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 { @@_cs }
+ \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:Nx \@@_compile_one:n
+ {
+ \flag_if_raised:nTF { @@_cs }
+ { \@@_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_raise_if_clear:n { @@_cs } }
+ \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} = { 0 } }
+ }
+ {
+ \prg_replicate:nn {#4}
+ { \char_generate:nn { \use_ii:nn #3 } {12} }
+ \@@_compile_cs_aux:NNnnnN
+ }
+ {
+ \quark_if_nil:NF #1
+ {
+ \flag_raise_if_clear:n { @@_cs }
+ \use_i_delimit_by_q_recursion_stop:nw
+ }
+ \use_none_delimit_by_q_recursion_stop:w
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Raw token lists with \cs{u}}
+%
+% \begin{macro}{\@@_compile_/u:}
+% \begin{macro}[EXP]{\@@_compile_u_loop:NN}
+% The |\u| escape is invalid in classes and directly following a
+% catcode test. Otherwise, it must be followed by a left brace. We
+% then collect the characters for the argument of |\u| within an
+% \texttt{x}-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_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_special:N \c_left_brace_str
+ {
+ \tl_set:Nx \l_@@_internal_a_tl { \if_false: } \fi:
+ \@@_compile_u_loop:NN
+ }
+ {
+ \__kernel_msg_error:nn { kernel } { u-missing-lbrace }
+ \@@_compile_raw:N u #1 #2
+ }
+ }
+ }
+\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: } \@@_compile_u_end: }
+ { #2 \@@_compile_u_loop:NN }
+ }
+ {
+ \if_false: { \fi: }
+ \__kernel_msg_error:nnx { kernel } { u-missing-rbrace } {#2}
+ \@@_compile_u_end:
+ #1 #2
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_compile_u_end:}
+% Once we have extracted the variable's name, we store the contents of
+% that variable 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:
+ {
+ \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:
+ {
+ \tl_gset:Nx \g_@@_internal_tl
+ {
+ \exp_args:No \__kernel_str_to_other_fast:n
+ { \l_@@_internal_a_tl }
+ }
+ \tl_build_put_right:Nx \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:Nx \l_@@_build_tl
+ {
+ \@@_class:NnnnN \c_true_bool
+ {
+ \if_int_compare:w "##3 = 0 \exp_stop_f:
+ \@@_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}{\@@_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_eq:NN \@@_anchor:N \@@_show_anchor_to_str:N
+ \cs_set_protected:Npn \@@_item_caseful_equal:n ##1
+ { \@@_show_one:n { char~code~\int_eval:n{##1} } }
+ \cs_set_protected:Npn \@@_item_caseful_range:nn ##1##2
+ {
+ \@@_show_one:n
+ { range~[\int_eval:n{##1}, \int_eval:n{##2}] }
+ }
+ \cs_set_protected:Npn \@@_item_caseless_equal:n ##1
+ { \@@_show_one:n { char~code~\int_eval:n{##1}~(caseless) } }
+ \cs_set_protected:Npn \@@_item_caseless_range:nn ##1##2
+ {
+ \@@_show_one:n
+ { Range~[\int_eval:n{##1}, \int_eval: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~##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}{\@@_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:Nx \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:Nx \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_set: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 = 0 }
+ {
+ \group_end:
+ \@@_show_one:n { \bool_if:NTF #1 { Fail } { Pass } }
+ }
+ {
+ \bool_if:nTF
+ { #1 && \int_compare_p:n { \l_@@_show_lines_int = 1 } }
+ {
+ \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:Nx \l_@@_build_tl
+ { \exp_not:o \l_@@_internal_a_tl }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_show_anchor_to_str:N}
+% The argument is an integer telling us where the anchor is. We
+% convert that to the relevant info.
+% \begin{macrocode}
+\cs_new:Npn \@@_show_anchor_to_str:N #1
+ {
+ anchor~at~
+ \str_case:nnF { #1 }
+ {
+ { \l_@@_min_pos_int } { start~(\iow_char:N\\A) }
+ { \l_@@_start_pos_int } { start~of~match~(\iow_char:N\\G) }
+ { \l_@@_max_pos_int } { end~(\iow_char:N\\Z) }
+ }
+ { <error:~'#1'~not~recognized> }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_show_item_catcode:NnT}
+% Produce a sequence of categories which the catcode bitmap |#2|
+% contains, and show it, indenting the tests on which this catcode
+% 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: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:} inserted at the start
+% of the regular expression to make 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:n} \Arg{key} where the \meta{key} is
+% a group number followed by |<| or |>| for the beginning or end of
+% group. This causes the current position in the query to be stored
+% as the \meta{key} submatch boundary.
+% \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:N}
+% 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.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_build:n #1
+ {
+ \@@_compile:n {#1}
+ \@@_build:N \l_@@_internal_regex
+ }
+\__kernel_patch:nnNNpn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build:N }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build:N
+ }
+\cs_new_protected:Npn \@@_build:N #1
+ {
+ \@@_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: }
+ \@@_group:nnnN {#1} { 1 } { 0 } \c_false_bool
+ \@@_toks_put_right:Nn \l_@@_right_state_int
+ { \@@_action_success: }
+ }
+% \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$;
+% \item \cs{g_@@_thread_state_intarray} from \cs{l_@@_min_active_int}
+% to $\cs{l_@@_max_active_int}-1$.
+% \end{itemize}
+% In fact, some data is stored in \tn{toks} registers (local) in the
+% same ranges so these ranges mustn't overlap. This is done by
+% setting \cs{l_@@_min_active_int} to \cs{l_@@_max_state_int} after
+% building the \textsc{nfa}. Here, in this nested call to the
+% matching code, we need the new versions of these ranges to involve
+% completely new entries of the intarray variables, so we begin by
+% setting (the new) \cs{l_@@_min_state_int} to (the old)
+% \cs{l_@@_max_active_int} to use higher entries.
+%
+% 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}
+\__kernel_patch:nnNNpn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_build_for_cs:n }
+ {
+ \@@_trace_states:n { 2 }
+ \@@_trace_pop:nnN { regex } { 1 } \@@_build_for_cs:n
+ }
+\cs_new_protected:Npn \@@_build_for_cs:n #1
+ {
+ \int_set_eq:NN \l_@@_min_state_int \l_@@_max_active_int
+ \int_set_eq:NN \l_@@_max_state_int \l_@@_min_state_int
+ \@@_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 \l_@@_curr_pos_int = \l_@@_max_pos_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:Nx #2 { #1 { \int_eval:n { #3 - #2 } } } }
+\cs_new_protected:Npn \@@_build_transition_right:nNn #1#2#3
+ { \@@_toks_put_right:Nx #2 { #1 { \int_eval:n { #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}
+\__kernel_patch:nnNNpn
+ {
+ \@@_trace:nnx { 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 }
+ }
+ }
+ { }
+\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:Nx \l_@@_left_state_int
+ {
+ \if_meaning:w \c_true_bool #1
+ #2 { \int_eval:n { #3 - \l_@@_left_state_int } }
+ #4 { \int_eval:n { #5 - \l_@@_left_state_int } }
+ \else:
+ #4 { \int_eval:n { #5 - \l_@@_left_state_int } }
+ #2 { \int_eval:n { #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:Npx \@@_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 = 0 \exp_stop_f:
+ \@@_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 - 1 }
+ \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}
+\__kernel_patch:nnNNpn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_group_aux:nnnnN }
+\cs_new_protected:Npn \@@_group_aux:nnnnN #1#2#3#4#5
+ {
+ \if_int_compare:w #3 = 0 \exp_stop_f:
+ \@@_build_new_state:
+%<assert>\assert_int:n { \l_@@_max_state_int = \l_@@_right_state_int + 1 }
+ \@@_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:Nf \@@_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}
+\__kernel_patch:nnNNpn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_branch:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_branch:n }
+\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 = 0 \exp_stop_f:
+ \int_set:Nn \l_@@_max_state_int
+ { \l_@@_left_state_int - 1 }
+ \@@_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 > - 1 \exp_stop_f:
+ \@@_toks_put_left:Nx #2 { \@@_action_submatch:n { #1 < } }
+ \@@_toks_put_left:Nx #3 { \@@_action_submatch:n { #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:n {#1} > 1 \exp_stop_f:
+ \int_set:Nn \l_@@_internal_c_int
+ {
+ ( #1 - 1 )
+ * ( \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 = 0 \exp_stop_f:
+ \@@_group_submatches:nNN {#1}
+ \l_@@_left_state_int \l_@@_right_state_int
+ \int_set:Nn \l_@@_internal_a_int
+ { \l_@@_left_state_int - 1 }
+ \@@_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 - 1 }
+ {
+ \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 = 0 \exp_stop_f:
+ \int_set:Nn \l_@@_right_state_int
+ { \l_@@_left_state_int - 1 }
+ \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:, \@@_anchor:N}
+% Usage: \cs{@@_assertion:Nn} \meta{boolean} \Arg{test}, where the
+% \meta{test} is either of the two other functions. Add a free
+% transition to a new state, conditionally to the assertion test. The
+% \cs{@@_b_test:} test is used by the |\b| and |\B| escape: check
+% if the last character was a word character or not, and do the same
+% to the current character. The boundary-markers of the string are
+% non-word characters for this purpose. Anchors at the start or end
+% of match use \cs{@@_anchor:N}, with a position controlled by the
+% integer |#1|.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_assertion:Nn #1#2
+ {
+ \@@_build_new_state:
+ \@@_toks_put_right:Nx \l_@@_left_state_int
+ {
+ \exp_not:n {#2}
+ \@@_break_point:TF
+ \bool_if:NF #1 { { } }
+ {
+ \@@_action_free:n
+ {
+ \int_eval:n
+ { \l_@@_right_state_int - \l_@@_left_state_int }
+ }
+ }
+ \bool_if:NT #1 { { } }
+ }
+ }
+\cs_new_protected:Npn \@@_anchor:N #1
+ {
+ \if_int_compare:w #1 = \l_@@_curr_pos_int
+ \exp_after:wN \@@_break_true:w
+ \fi:
+ }
+\cs_new_protected:Npn \@@_b_test:
+ {
+ \group_begin:
+ \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: }
+ }
+% \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:Nx \l_@@_left_state_int
+ {
+ \@@_action_submatch:n { 0< }
+ \bool_set_true:N \l_@@_fresh_thread_bool
+ \@@_action_free:n
+ {
+ \int_eval:n
+ { \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_state_intarray}: 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
+% don't start from $0$ because the \tn{toks} registers with low
+% numbers are used to hold the states of the \textsc{nfa}. We match
+% without backtracking, keeping all threads in lockstep at the
+% \texttt{current_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_@@_last_char_int,
+% \l_@@_case_changed_char_int
+% }
+% The character and category codes of the token at the current
+% position; the character code of the token at the previous position;
+% and the character code of the result of changing the case of the
+% current token (|A-Z|$\leftrightarrow$|a-z|). This last integer is
+% only computed when necessary, and is otherwise \cs{c_max_int}. The
+% \texttt{current_char} variable is also used in various other phases
+% to hold a character code.
+% \begin{macrocode}
+\int_new:N \l_@@_curr_char_int
+\int_new:N \l_@@_curr_catcode_int
+\int_new:N \l_@@_last_char_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_prop, \l_@@_success_submatches_prop}
+% The submatches for the thread which is currently active are stored
+% in the \texttt{current_submatches} property list variable. This
+% property list is stored by \cs{@@_action_cost:n} into the
+% \tn{toks} register for the target state of the transition, to be
+% retrieved when matching at the next position. When a thread
+% succeeds, this property list is copied to
+% \cs{l_@@_success_submatches_prop}: only the last successful thread
+% remains there.
+% \begin{macrocode}
+\prop_new:N \l_@@_curr_submatches_prop
+\prop_new:N \l_@@_success_submatches_prop
+% \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_active_int, \l_@@_max_active_int}
+% All the currently active threads are kept in order of precedence in
+% \cs{g_@@_thread_state_intarray}, and the corresponding submatches in the
+% \tn{toks}. For our purposes, those serve as an array, indexed from
+% \texttt{min_active} (inclusive) to \texttt{max_active} (excluded).
+% At the start of every step, the whole array is unpacked, so that the
+% space can immediately be reused, and \texttt{max_active} is reset to
+% \texttt{min_active}, effectively clearing the array.
+% \begin{macrocode}
+\int_new:N \l_@@_min_active_int
+\int_new:N \l_@@_max_active_int
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_state_active_intarray, \g_@@_thread_state_intarray}
+% \cs{g_@@_state_active_intarray} stores the last \meta{step} in which
+% each \meta{state} was active. \cs{g_@@_thread_state_intarray} stores
+% threads to be considered in the next step, more precisely the
+% states in which these threads are.
+% \begin{macrocode}
+\intarray_new:Nn \g_@@_state_active_intarray { 65536 }
+\intarray_new:Nn \g_@@_thread_state_intarray { 65536 }
+% \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:}
+% First store the query into \tn{toks} registers and arrays (see
+% \cs{@@_query_set:nnn}). Then 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}
+\__kernel_patch:nnNNpn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match:n
+ \@@_trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match:n }
+\cs_new_protected:Npn \@@_match:n #1
+ {
+ \int_zero:N \l_@@_balance_int
+ \int_set:Nn \l_@@_curr_pos_int { 2 * \l_@@_max_state_int }
+ \@@_query_set:nnn { } { -1 } { -2 }
+ \int_set_eq:NN \l_@@_min_pos_int \l_@@_curr_pos_int
+ \tl_analysis_map_inline:nn {#1}
+ { \@@_query_set:nnn {##1} {"##3} {##2} }
+ \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int
+ \@@_query_set:nnn { } { -1 } { -2 }
+ \@@_match_init:
+ \@@_match_once:
+ }
+\__kernel_patch:nnNNpn
+ {
+ \@@_trace_push:nnN { regex } { 1 } \@@_match_cs:n
+ \@@_trace:nnx { regex } { 1 } { analyzing~query~token~list }
+ }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_match_cs:n }
+\cs_new_protected:Npn \@@_match_cs:n #1
+ {
+ \int_zero:N \l_@@_balance_int
+ \int_set:Nn \l_@@_curr_pos_int
+ {
+ \int_max:nn { 2 * \l_@@_max_state_int - \l_@@_min_state_int }
+ { \l_@@_max_pos_int }
+ + 1
+ }
+ \@@_query_set:nnn { } { -1 } { -2 }
+ \int_set_eq:NN \l_@@_min_pos_int \l_@@_curr_pos_int
+ \str_map_inline:nn {#1}
+ {
+ \@@_query_set:nnn { \exp_not:n {##1} }
+ { \tl_if_blank:nTF {##1} { 10 } { 12 } }
+ { `##1 }
+ }
+ \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int
+ \@@_query_set:nnn { } { -1 } { -2 }
+ \@@_match_init:
+ \@@_match_once:
+ }
+\__kernel_patch:nnNNpn
+ { \@@_trace:nnx { regex } { 1 } { initializing } }
+ { }
+\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 - 1 }
+ {
+ \__kernel_intarray_gset:Nnn
+ \g_@@_state_active_intarray {##1} { 1 }
+ }
+ \int_set_eq:NN \l_@@_min_active_int \l_@@_max_state_int
+ \int_zero:N \l_@@_step_int
+ \int_set_eq:NN \l_@@_success_pos_int \l_@@_min_pos_int
+ \int_set:Nn \l_@@_min_submatch_int
+ { 2 * \l_@@_max_state_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:}
+% This function finds one match, then does some action defined by the
+% \texttt{every_match} token list, which may recursively call
+% \cs{@@_match_once:}. First initialize some variables: set the
+% conditional which detects identical empty matches; this match
+% attempt starts at the previous \texttt{success_pos}, is not yet
+% successful, and has no submatches yet; clear the array of active
+% threads, and put the starting state $0$ in it. We are then almost
+% ready to read our first token in the query, but we actually start
+% one position earlier than the start, and \texttt{get} that token, to
+% set \texttt{last_char} properly for word
+% boundaries. Then call \cs{@@_match_loop:}, which runs through the
+% query until the end or until a successful match breaks early.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_match_once:
+ {
+ \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
+ \prop_clear:N \l_@@_curr_submatches_prop
+ \int_set_eq:NN \l_@@_max_active_int \l_@@_min_active_int
+ \@@_store_state:n { \l_@@_min_state_int }
+ \int_set:Nn \l_@@_curr_pos_int
+ { \l_@@_start_pos_int - 1 }
+ \@@_query_get:
+ \@@_match_loop:
+ \l_@@_every_match_tl
+ }
+% \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
+ }
+ }
+\cs_new_protected:Npn \@@_multi_match:n #1
+ {
+ \tl_set:Nn \l_@@_every_match_tl
+ {
+ \if_meaning:w \c_true_bool \l_@@_match_success_bool
+ \bool_gset_true:N \g_@@_success_bool
+ #1
+ \exp_after:wN \@@_match_once:
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_match_loop:}
+% \begin{macro}[rEXP]{\@@_match_one_active:n}
+% At each new position, set some variables and get the new character
+% and category from the query. Then unpack the array of active
+% threads, and clear it by resetting its length
+% (\texttt{max_active}). This results in a sequence of
+% \cs{@@_use_state_and_submatches:nn} \Arg{state} \Arg{prop}, and
+% we consider those states one by one in order. As soon as a thread
+% succeeds, exit the step, and, if there are threads to consider at the
+% next position, and we have not reached the end of the string,
+% repeat the loop. Otherwise, the last thread that succeeded is what
+% \cs{@@_match_once:} matches. We explain the \texttt{fresh_thread}
+% business when describing \cs{@@_action_wildcard:}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_match_loop:
+ {
+ \int_add:Nn \l_@@_step_int { 2 }
+ \int_incr:N \l_@@_curr_pos_int
+ \int_set_eq:NN \l_@@_last_char_int \l_@@_curr_char_int
+ \int_set_eq:NN \l_@@_case_changed_char_int \c_max_int
+ \@@_query_get:
+ \use:x
+ {
+ \int_set_eq:NN \l_@@_max_active_int \l_@@_min_active_int
+ \int_step_function:nnN
+ { \l_@@_min_active_int }
+ { \l_@@_max_active_int - 1 }
+ \@@_match_one_active:n
+ }
+ \prg_break_point:
+ \bool_set_false:N \l_@@_fresh_thread_bool
+ \if_int_compare:w \l_@@_max_active_int > \l_@@_min_active_int
+ \if_int_compare:w \l_@@_curr_pos_int < \l_@@_max_pos_int
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_match_loop:
+ \fi:
+ \fi:
+ }
+\cs_new:Npn \@@_match_one_active:n #1
+ {
+ \@@_use_state_and_submatches:nn
+ { \__kernel_intarray_item:Nn \g_@@_thread_state_intarray {#1} }
+ { \@@_toks_use:w #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_query_set:nnn}
+% The arguments are: tokens that \texttt{o} and \texttt{x} expand to
+% one token of the query, the catcode, and the character code. Store
+% those, and the current brace balance (used later to check for
+% overall brace balance) in a \tn{toks} register and some arrays,
+% then update the \texttt{balance}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_query_set:nnn #1#2#3
+ {
+ \__kernel_intarray_gset:Nnn \g_@@_charcode_intarray
+ { \l_@@_curr_pos_int } {#3}
+ \__kernel_intarray_gset:Nnn \g_@@_catcode_intarray
+ { \l_@@_curr_pos_int } {#2}
+ \__kernel_intarray_gset:Nnn \g_@@_balance_intarray
+ { \l_@@_curr_pos_int } { \l_@@_balance_int }
+ \@@_toks_set:Nn \l_@@_curr_pos_int {#1}
+ \int_incr:N \l_@@_curr_pos_int
+ \if_case:w #2 \exp_stop_f:
+ \or: \int_incr:N \l_@@_balance_int
+ \or: \int_decr:N \l_@@_balance_int
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_query_get:}
+% Extract the current character and category codes at the current
+% position from the appropriate arrays.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_query_get:
+ {
+ \l_@@_curr_char_int
+ = \__kernel_intarray_item:Nn \g_@@_charcode_intarray
+ { \l_@@_curr_pos_int } \scan_stop:
+ \l_@@_curr_catcode_int
+ = \__kernel_intarray_item:Nn \g_@@_catcode_intarray
+ { \l_@@_curr_pos_int } \scan_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Using states of the \textsc{nfa}}
+%
+% \begin{macro}{\@@_use_state:}
+% 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}
+\__kernel_patch:nnNNpn
+ {
+ \@@_trace:nnx { regex } { 2 }
+ { state~\int_use:N \l_@@_curr_state_int }
+ }
+ { }
+\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:n { \l_@@_step_int + 1 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_use_state_and_submatches:nn}
+% This function is called as one item in the array of active threads
+% after that array has been unpacked for a new step. Update the
+% \texttt{current_state} and \texttt{current_submatches} and use the
+% state if it has not yet been encountered at this step.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_use_state_and_submatches:nn #1 #2
+ {
+ \int_set:Nn \l_@@_curr_state_int {#1}
+ \if_int_compare:w
+ \__kernel_intarray_item:Nn \g_@@_state_active_intarray
+ { \l_@@_curr_state_int }
+ < \l_@@_step_int
+ \tl_set:Nn \l_@@_curr_submatches_prop {#2}
+ \exp_after:wN \@@_use_state:
+ \fi:
+ \scan_stop:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Actions when matching}
+%
+% \begin{macro}{\@@_action_start_wildcard:}
+% For an unanchored match, state $0$ has a free transition to the next
+% and a costly one to itself, to repeat at the next position. To catch
+% repeated identical empty matches, we need to know if a successful
+% thread corresponds to an empty match. The instruction resetting
+% \cs{l_@@_fresh_thread_bool} may be skipped by a successful
+% thread, hence we had to add it to \cs{@@_match_loop:} too.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_action_start_wildcard:
+ {
+ \bool_set_true:N \l_@@_fresh_thread_bool
+ \@@_action_free:n {1}
+ \bool_set_false:N \l_@@_fresh_thread_bool
+ \@@_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:x
+ {
+ \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_prop
+ { \exp_not:o \l_@@_curr_submatches_prop }
+ }
+ }
+% \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:Nx \@@_store_state:n
+ { \int_eval:n { \l_@@_curr_state_int + #1 } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_store_state:n}
+% \begin{macro}{\@@_store_submatches:}
+% Put the given state in \cs{g_@@_thread_state_intarray}, and increment
+% the length of the array. Also store the current submatch in the
+% appropriate \tn{toks}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_store_state:n #1
+ {
+ \@@_store_submatches:
+ \__kernel_intarray_gset:Nnn \g_@@_thread_state_intarray
+ { \l_@@_max_active_int } {#1}
+ \int_incr:N \l_@@_max_active_int
+ }
+\cs_new_protected:Npn \@@_store_submatches:
+ {
+ \@@_toks_set:No \l_@@_max_active_int
+ { \l_@@_curr_submatches_prop }
+ }
+% \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: { }
+ \cs_set_protected:Npn \@@_action_submatch:n ##1 { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_action_submatch:n}
+% Update the current submatches with the information from the current
+% position. Maybe a bottleneck.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_action_submatch:n #1
+ {
+ \prop_put:Nno \l_@@_curr_submatches_prop {#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
+ \prop_set_eq:NN \l_@@_success_submatches_prop
+ \l_@@_curr_submatches_prop
+ \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}{\l_@@_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 \l_@@_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{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}
+%
+% \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{x}-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:
+ \exp_after:wN \prg_break:
+ \fi:
+ \@@_toks_use:w #1 \exp_stop_f:
+ \exp_after:wN \@@_query_range_loop:ww
+ \int_value:w \@@_int_eval:w #1 + 1 ; #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.
+%^^A todo: understand when these int_compare are needed
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_submatch_balance:n #1
+ {
+ \int_eval:n
+ {
+ \int_compare:nNnTF
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_end_intarray {#1}
+ }
+ = 0
+ { 0 }
+ {
+ \__kernel_intarray_item:Nn \g_@@_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_end_intarray {#1}
+ }
+ }
+ -
+ \int_compare:nNnTF
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_begin_intarray {#1}
+ }
+ = 0
+ { 0 }
+ {
+ \__kernel_intarray_item:Nn \g_@@_balance_intarray
+ {
+ \__kernel_intarray_item:Nn
+ \g_@@_submatch_begin_intarray {#1}
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Framework}
+%
+% \begin{macro}{\@@_replacement:n}
+% \begin{macro}{\@@_replacement_aux: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{l_@@_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}
+\__kernel_patch:nnNNpn
+ { \@@_trace_push:nnN { regex } { 1 } \@@_replacement:n }
+ { \@@_trace_pop:nnN { regex } { 1 } \@@_replacement:n }
+\cs_new_protected:Npn \@@_replacement:n #1
+ {
+ \group_begin:
+ \tl_build_begin:N \l_@@_build_tl
+ \int_zero:N \l_@@_balance_int
+ \tl_clear:N \l_@@_balance_tl
+ \@@_escape_use:nnnn
+ {
+ \if_charcode:w \c_right_brace_str ##1
+ \@@_replacement_rbrace:N
+ \else:
+ \@@_replacement_normal:n
+ \fi:
+ ##1
+ }
+ { \@@_replacement_escaped:N ##1 }
+ { \@@_replacement_normal:n ##1 }
+ {#1}
+ \prg_do_nothing: \prg_do_nothing:
+ \if_int_compare:w \l_@@_replacement_csnames_int > 0 \exp_stop_f:
+ \__kernel_msg_error:nnx { kernel } { replacement-missing-rbrace }
+ { \int_use:N \l_@@_replacement_csnames_int }
+ \tl_build_put_right:Nx \l_@@_build_tl
+ { \prg_replicate:nn \l_@@_replacement_csnames_int \cs_end: }
+ \fi:
+ \seq_if_empty:NF \l_@@_replacement_category_seq
+ {
+ \__kernel_msg_error:nnx { kernel } { replacement-missing-rparen }
+ { \seq_count:N \l_@@_replacement_category_seq }
+ \seq_clear:N \l_@@_replacement_category_seq
+ }
+ \cs_gset:Npx \@@_replacement_balance_one_match:n ##1
+ {
+ + \int_use:N \l_@@_balance_int
+ \l_@@_balance_tl
+ - \@@_submatch_balance:n {##1}
+ }
+ \tl_build_end:N \l_@@_build_tl
+ \exp_args:NNo
+ \group_end:
+ \@@_replacement_aux:n \l_@@_build_tl
+ }
+\cs_new_protected:Npn \@@_replacement_aux: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
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_replacement_normal:n}
+% Most characters are simply sent to the output by
+% \cs{tl_build_put_right:Nn}, unless a particular category code has been
+% 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}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_normal:n #1
+ {
+ \tl_if_empty:NTF \l_@@_replacement_category_tl
+ { \tl_build_put_right:Nn \l_@@_build_tl {#1} }
+ { % (
+ \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
+ }
+ \@@_replacement_normal:n {#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.
+% We use \cs{token_to_str:N} to give spaces the right category code.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_escaped:N #1
+ {
+ \cs_if_exist_use:cF { @@_replacement_#1:w }
+ {
+ \if_int_compare:w 1 < 1#1 \exp_stop_f:
+ \@@_replacement_put_submatch:n {#1}
+ \else:
+ \exp_args:No \@@_replacement_normal:n
+ { \token_to_str:N #1 }
+ \fi:
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Submatches}
+%
+% \begin{macro}{\@@_replacement_put_submatch: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. There is an \cs{exp_not:N} here as at the point-of-use
+% of \cs{l_@@_balance_tl} there is an \texttt{x}-type expansion which is needed
+% to get |##1| in correctly.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replacement_put_submatch:n #1
+ {
+ \if_int_compare:w #1 < \l_@@_capturing_group_int
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \@@_query_submatch:n { \int_eval:n { #1 + ##1 } } }
+ \if_int_compare:w \l_@@_replacement_csnames_int = 0 \exp_stop_f:
+ \tl_put_right:Nn \l_@@_balance_tl
+ {
+ + \@@_submatch_balance:n
+ { \exp_not:N \int_eval:n { #1 + ##1 } }
+ }
+ \fi:
+ \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
+ {
+ \@@_two_if_eq:NNNNTF
+ #1 #2 \@@_replacement_normal:n \c_left_brace_str
+ { \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 1 < 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
+ {
+ \exp_after:wN \token_if_eq_charcode:NNTF \c_left_brace_str #2
+ { \@@_replacement_cu_aux:Nw \@@_replacement_exp_not:N }
+ {
+ \cs_if_exist:cTF { @@_replacement_c_#2:w }
+ { \@@_replacement_cat:NNN #2 }
+ { \@@_replacement_error:NNN c #1#2 }
+ }
+ }
+ { \@@_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
+ {
+ \@@_two_if_eq:NNNNTF
+ #1 #2 \@@_replacement_normal:n \c_left_brace_str
+ { \@@_replacement_cu_aux:Nw \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 > 0 \exp_stop_f:
+ \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}
+%
+% \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
+ { \__kernel_msg_error:nn { kernel } { replacement-catcode-end } }
+ {
+ \int_compare:nNnTF { \l_@@_replacement_csnames_int } > 0
+ {
+ \__kernel_msg_error:nnnn
+ { kernel } { 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
+ {
+ \__kernel_msg_error:nnnn
+ { kernel } { 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 0 = `#3 \scan_stop:
+ \tex_lowercase:D { \tl_build_put_right:Nn \l_@@_build_tl {#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{x}-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{x}-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 = 0 \exp_stop_f:
+ \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{x}-expansions.
+% \begin{macrocode}
+ \cs_new_protected:Npn \@@_replacement_c_C:w #1#2
+ {
+ \tl_build_put_right:Nn \l_@@_build_tl
+ { \exp_not:N \exp_not:N \exp_not:c {#2} }
+ }
+% \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{x}-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 = 0 \exp_stop_f:
+ \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{x}-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 = 0 \exp_stop_f:
+ \__kernel_msg_error:nn { kernel } { replacement-null-space }
+ \fi:
+ \tex_lccode:D `\ = `#2 \scan_stop:
+ \tex_lowercase:D { \tl_build_put_right:Nn \l_@@_build_tl {~} }
+ }
+% \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
+ {
+ \__kernel_msg_error:nnx { kernel } { 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:Nx #1 { \exp_not:o \l_@@_internal_regex }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\regex_show:N, \regex_show:n}
+% User functions: the \texttt{n} variant requires compilation first.
+% Then show the variable with some appropriate text. The auxiliary
+% is defined in a different section.
+% \begin{macrocode}
+\cs_new_protected:Npn \regex_show:n #1
+ {
+ \@@_compile:n {#1}
+ \@@_show:N \l_@@_internal_regex
+ \msg_show:nnxxxx { LaTeX / kernel } { show-regex }
+ { \tl_to_str:n {#1} } { }
+ { \l_@@_internal_a_tl } { }
+ }
+\cs_new_protected:Npn \regex_show:N #1
+ {
+ \__kernel_chk_defined:NT #1
+ {
+ \@@_show:N #1
+ \msg_show:nnxxxx { LaTeX / kernel } { show-regex }
+ { } { \token_to_str:N #1 }
+ { \l_@@_internal_a_tl } { }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\regex_match:nn, \regex_match:Nn}
+% 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_new_protected_conditional:Npnn \regex_match:Nn #1#2 { T , F , TF }
+ {
+ \@@_if_match:nn { \@@_build:N #1 } {#2}
+ \@@_return:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\regex_count:nnN, \regex_count:NnN}
+% 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 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[noTF]
+% {
+% \regex_extract_once:nnN, \regex_extract_once:NnN,
+% \regex_extract_all:nnN, \regex_extract_all:NnN,
+% \regex_replace_once:nnN, \regex_replace_once:NnN,
+% \regex_replace_all:nnN, \regex_replace_all:NnN,
+% \regex_split:nnN, \regex_split:NnN
+% }
+% 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: }
+ }
+\@@_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}
+%
+% \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}{@@_begin, @@_end}
+% Those flags are raised to indicate extra begin-group
+% or end-group tokens when extracting submatches.
+% \begin{macrocode}
+\flag_new:n { @@_begin }
+\flag_new:n { @@_end }
+% \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}
+% Hold the place where the match attempt begun and the end-points of each submatch.
+% \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 }
+% \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}
+%
+% \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}{\@@_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:
+ \@@_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}
+ \@@_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 } { 0 }
+ \__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}
+%<assert>\assert_int:n { \l_@@_curr_pos_int = \l_@@_max_pos_int }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ { \l_@@_submatch_int } { 0 }
+ \__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}
+% 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{l_@@_internal_a_tl}. We detect unbalanced results using
+% the two flags \texttt{__regex_begin} and \texttt{__regex_end}, raised
+% whenever we see too many begin-group or end-group tokens in a
+% submatch.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_end_extract_seq:N #1
+ {
+ \flag_clear:n { @@_begin }
+ \flag_clear:n { @@_end }
+ \seq_set_from_function:NnN \l_@@_internal_seq
+ {
+ \int_step_function:nnN { \l_@@_min_submatch_int }
+ { \l_@@_submatch_int - 1 }
+ }
+ \@@_extract_seq_aux:n
+ \int_compare:nNnF
+ {
+ \flag_height:n { @@_begin } +
+ \flag_height:n { @@_end }
+ }
+ = 0
+ {
+ \__kernel_msg_error:nnxxx { kernel } { result-unbalanced }
+ { splitting~or~extracting~submatches }
+ { \flag_height:n { @@_end } }
+ { \flag_height:n { @@_begin } }
+ }
+ \seq_set_map:NNn \l_@@_internal_seq \l_@@_internal_seq {##1}
+ \exp_args:NNNo
+ \group_end:
+ \tl_set:Nn #1 { \l_@@_internal_seq }
+ }
+% \end{macrocode}
+% \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
+ {
+ \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 < 0 \exp_stop_f:
+ \flag_raise:n { @@_end }
+ \prg_replicate:nn {-#1} { \exp_not:n { { \if_false: } \fi: } }
+ \fi:
+ \@@_query_submatch:n {#2}
+ \if_int_compare:w #1 > 0 \exp_stop_f:
+ \flag_raise:n { @@_begin }
+ \prg_replicate:nn {#1} { \exp_not:n { \if_false: { \fi: } } }
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {\@@_extract:, \@@_extract_b:wn, \@@_extract_e:wn}
+% Our task here is to extract from the property list
+% \cs{l_@@_success_submatches_prop} the list of end-points of
+% submatches, and store them in appropriate array entries, from
+% \cs{l_@@_zeroth_submatch_int} upwards. We begin by emptying those
+% entries. Then for each \meta{key}--\meta{value} pair in
+% the property list update the appropriate entry. This
+% is somewhat a hack: the \meta{key} is a non-negative integer
+% followed by |<| or |>|, which we use in a comparison to $-1$. At the
+% end, store the information about the position at which the match
+% attempt started, in \cs{g_@@_submatch_prev_intarray}.
+% \begin{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_begin_intarray
+ { \l_@@_submatch_int } { 0 }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray
+ { \l_@@_submatch_int } { 0 }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ { \l_@@_submatch_int } { 0 }
+ \int_incr:N \l_@@_submatch_int
+ }
+ \prop_map_inline:Nn \l_@@_success_submatches_prop
+ {
+ \if_int_compare:w ##1 - 1 \exp_stop_f:
+ \exp_after:wN \@@_extract_e:wn \int_value:w
+ \else:
+ \exp_after:wN \@@_extract_b:wn \int_value:w
+ \fi:
+ \@@_int_eval:w \l_@@_zeroth_submatch_int + ##1 {##2}
+ }
+ \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray
+ { \l_@@_zeroth_submatch_int } { \l_@@_start_pos_int }
+ \fi:
+ }
+\cs_new_protected:Npn \@@_extract_b:wn #1 < #2
+ {
+ \__kernel_intarray_gset:Nnn
+ \g_@@_submatch_begin_intarray {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_extract_e:wn #1 > #2
+ { \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray {#1} {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Replacement}
+%
+% \begin{macro}{\@@_replace_once: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{x}-expansion, and checks that braces are balanced in the
+% final result.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_replace_once:nnN #1#2#3
+ {
+ \group_begin:
+ \@@_single_match:
+ #1
+ \@@_replacement:n {#2}
+ \exp_args:No \@@_match:n { #3 }
+ \if_meaning:w \c_false_bool \g_@@_success_bool
+ \group_end:
+ \else:
+ \@@_extract:
+ \int_set:Nn \l_@@_balance_int
+ {
+ \@@_replacement_balance_one_match:n
+ { \l_@@_zeroth_submatch_int }
+ }
+ \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
+ \fi:
+ }
+% \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#3
+ {
+ \group_begin:
+ \@@_multi_match:n { \@@_extract: }
+ #1
+ \@@_replacement:n {#2}
+ \exp_args:No \@@_match:n {#3}
+ \int_set:Nn \l_@@_balance_int
+ {
+ 0
+ \int_step_function:nnnN
+ { \l_@@_min_submatch_int }
+ \l_@@_capturing_group_int
+ { \l_@@_submatch_int - 1 }
+ \@@_replacement_balance_one_match:n
+ }
+ \tl_set:Nx \l_@@_internal_a_tl
+ {
+ \int_step_function:nnnN
+ { \l_@@_min_submatch_int }
+ \l_@@_capturing_group_int
+ { \l_@@_submatch_int - 1 }
+ \@@_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}
+% If the brace balance is not $0$, raise an error. Then set the user's
+% variable |#1| to the \texttt{x}-expansion of
+% \cs{l_@@_internal_a_tl}, adding the appropriate braces to produce
+% a balanced result. And end the group.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_group_end_replace:N #1
+ {
+ \if_int_compare:w \l_@@_balance_int = 0 \exp_stop_f:
+ \else:
+ \__kernel_msg_error:nnxxx { kernel } { result-unbalanced }
+ { replacing }
+ { \int_max:nn { - \l_@@_balance_int } { 0 } }
+ { \int_max:nn { \l_@@_balance_int } { 0 } }
+ \fi:
+ \use:x
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N #1
+ {
+ \if_int_compare:w \l_@@_balance_int < 0 \exp_stop_f:
+ \prg_replicate:nn { - \l_@@_balance_int }
+ { { \if_false: } \fi: }
+ \fi:
+ \l_@@_internal_a_tl
+ \if_int_compare:w \l_@@_balance_int > 0 \exp_stop_f:
+ \prg_replicate:nn { \l_@@_balance_int }
+ { \if_false: { \fi: } }
+ \fi:
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Storing and showing compiled patterns}
+%
+% \subsection{Messages}
+%
+% Messages for the preparsing phase.
+% \begin{macrocode}
+\use:x
+ {
+ \__kernel_msg_new:nnn { kernel } { trailing-backslash }
+ { Trailing~escape~char~'\iow_char:N\\'~in~regex~or~replacement. }
+ \__kernel_msg_new:nnn { kernel } { x-missing-rbrace }
+ {
+ Missing~brace~'\iow_char:N\}'~in~regex~
+ '...\iow_char:N\\x\iow_char:N\{...##1'.
+ }
+ \__kernel_msg_new:nnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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[...]'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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\}'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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?
+ }
+\__kernel_msg_new:nnnn { kernel } { 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:]'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { unknown-option }
+ { Unknown~option~'#1'~for~regular~expressions. }
+ {
+ The~only~available~option~is~'case-insensitive',~toggled~by~
+ '(?i)'~and~'(?-i)'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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'.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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 } .
+ }
+\__kernel_msg_new:nnnn { kernel } { 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 } .
+ }
+% \end{macrocode}
+%
+% Used when showing a regex.
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { show-regex }
+ {
+ >~Compiled~regex~
+ \tl_if_empty:nTF {#1} { variable~ #2 } { {#1} } :
+ #3
+ }
+% \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:nnx}
+% 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}
+\__kernel_if_debug:TF
+ {
+ \cs_new_protected:Npn \@@_trace_push:nnN #1#2#3
+ { \@@_trace:nnx {#1} {#2} { entering~ \token_to_str:N #3 } }
+ \cs_new_protected:Npn \@@_trace_pop:nnN #1#2#3
+ { \@@_trace:nnx {#1} {#2} { leaving~ \token_to_str:N #3 } }
+ \cs_new_protected:Npn \@@_trace:nnx #1#2#3
+ {
+ \int_compare:nNnF
+ { \int_use:c { g_@@_trace_#1_int } } < {#2}
+ { \iow_term:x { 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}
+\__kernel_if_debug:TF
+ {
+ \cs_new_protected:Npn \@@_trace_states:n #1
+ {
+ \int_step_inline:nnn
+ \l_@@_min_state_int
+ { \l_@@_max_state_int - 1 }
+ {
+ \@@_trace:nnx { regex } {#1}
+ { \iow_char:N \\toks ##1 = { \@@_toks_use:w ##1 } }
+ }
+ }
+ }
+ { }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|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 [[:xxx:]] positive POSIX named set
+%^^A [[:^xxx:]] negative POSIX named set
+%^^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 (*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 (?JmsUx) options (duplicate names; multiline; single line;
+%^^A ungreedy; extended)
+%^^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/macros/latex/contrib/l3kernel/l3seq.dtx b/macros/latex/contrib/l3kernel/l3seq.dtx
new file mode 100644
index 0000000000..2a0083bd6a
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3seq.dtx
@@ -0,0 +1,2070 @@
+% \iffalse meta-comment
+%
+%% File: l3seq.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Sequences and stacks^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{sequence}
+% \end{syntax}
+% Creates a new \meta{sequence} or raises an error if the name is
+% already taken. The declaration is global. The \meta{sequence}
+% 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{sequence}
+% \end{syntax}
+% Clears all items from the \meta{sequence}.
+% \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{sequence}
+% \end{syntax}
+% Ensures that the \meta{sequence} 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{sequence} 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{sequence_1} \meta{sequence_2}
+% \end{syntax}
+% Sets the content of \meta{sequence_1} equal to that of
+% \meta{sequence_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{sequence} \meta{comma-list}
+% \end{syntax}
+% Converts the data in the \meta{comma list} into a \meta{sequence}:
+% the original \meta{comma list} is unchanged.
+% \end{function}
+%
+% \begin{function}[added = 2011-08-15, updated = 2012-07-02]
+% {
+% \seq_set_split:Nnn , \seq_set_split:NnV ,
+% \seq_gset_split:Nnn, \seq_gset_split:NnV
+% }
+% \begin{syntax}
+% \cs{seq_set_split:Nnn} \meta{sequence} \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{sequence}.
+% 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{sequence} \Arg{}.
+% 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}.
+% \end{function}
+%
+% \begin{function}
+% {\seq_concat:NNN, \seq_concat:ccc, \seq_gconcat:NNN, \seq_gconcat:ccc}
+% \begin{syntax}
+% \cs{seq_concat:NNN} \meta{sequence_1} \meta{sequence_2} \meta{sequence_3}
+% \end{syntax}
+% Concatenates the content of \meta{sequence_2} and \meta{sequence_3}
+% together and saves the result in \meta{sequence_1}. The items in
+% \meta{sequence_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{sequence}
+% \cs{seq_if_exist:NTF} \meta{sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests whether the \meta{sequence} is currently defined. This does not
+% check that the \meta{sequence} 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:No, \seq_put_left:Nx,
+% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv,
+% \seq_put_left:co, \seq_put_left:cx,
+% \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv,
+% \seq_gput_left:No, \seq_gput_left:Nx,
+% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv,
+% \seq_gput_left:co, \seq_gput_left:cx
+% }
+% \begin{syntax}
+% \cs{seq_put_left:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the left of the \meta{sequence}.
+% \end{function}
+%
+% \begin{function}{
+% \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv,
+% \seq_put_right:No, \seq_put_right:Nx,
+% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv,
+% \seq_put_right:co, \seq_put_right:cx,
+% \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv,
+% \seq_gput_right:No, \seq_gput_right:Nx,
+% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv,
+% \seq_gput_right:co, \seq_gput_right:cx
+% }
+% \begin{syntax}
+% \cs{seq_put_right:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Appends the \meta{item} to the right of the \meta{sequence}.
+% \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{sequence} \meta{token list variable}
+% \end{syntax}
+% Stores the left-most item from a \meta{sequence} in the
+% \meta{token list variable} without removing it from the
+% \meta{sequence}. The \meta{token list variable} is assigned locally.
+% If \meta{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Stores the right-most item from a \meta{sequence} in the
+% \meta{token list variable} without removing it from the
+% \meta{sequence}. The \meta{token list variable} is assigned locally.
+% If \meta{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{sequence} 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{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the left-most item from a \meta{sequence} 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{sequence} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% If \meta{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the right-most item from a \meta{sequence} 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{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the right-most item from a \meta{sequence} 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{sequence} is modified globally, while the assignment of
+% the \meta{token list variable} is local.
+% If \meta{sequence} 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:cn}
+% \begin{syntax}
+% \cs{seq_item:Nn} \meta{sequence} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{sequence} 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{sequence} (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{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{sequence}. If the
+% \meta{sequence} 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{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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, stores the left-most item from the
+% \meta{sequence}
+% in the \meta{token list variable} without removing it from the
+% \meta{sequence}, 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, stores the right-most item from the
+% \meta{sequence}
+% in the \meta{token list variable} without removing it from the
+% \meta{sequence}, 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the left-most item from the
+% \meta{sequence}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{sequence}, then leaves the \meta{true code} in the input stream.
+% Both the \meta{sequence} 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the left-most item from the \meta{sequence}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{sequence}, then leaves the \meta{true code} in the input stream.
+% The \meta{sequence} 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the right-most item from the \meta{sequence}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{sequence}, then leaves the \meta{true code} in the input stream.
+% Both the \meta{sequence} 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the right-most item from the \meta{sequence}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{sequence}, then leaves the \meta{true code} in the input stream.
+% The \meta{sequence} 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{sequence}
+% \end{syntax}
+% Removes duplicate items from the \meta{sequence}, leaving the
+% left most copy of each item in the \meta{sequence}. 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{sequence} 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:cn,
+% \seq_gremove_all:Nn, \seq_gremove_all:cn
+% }
+% \begin{syntax}
+% \cs{seq_remove_all:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Removes every occurrence of \meta{item} from the \meta{sequence}.
+% The \meta{item} comparison takes place on a token basis, as for
+% \cs{tl_if_eq:nnTF}.
+% \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{sequence}
+% \end{syntax}
+% Reverses the order of the items stored in the \meta{sequence}.
+% \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{sequence} \Arg{comparison code}
+% \end{syntax}
+% Sorts the items in the \meta{sequence} according to the
+% \meta{comparison code}, and assigns the result to
+% \meta{sequence}. The details of sorting comparison are
+% described in Section~\ref{sec:l3sort:mech}.
+% \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{sequence}
+% \cs{seq_if_empty:NTF} \meta{sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{sequence} 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:No, \seq_if_in:Nx,
+% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \seq_if_in:co, \seq_if_in:cx
+% }
+% \begin{syntax}
+% \cs{seq_if_in:NnTF} \meta{sequence} \Arg{item} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{item} is present in the \meta{sequence}.
+% \end{function}
+%
+% \section{Mapping to 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{sequence} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{item} stored in the
+% \meta{sequence}. The \meta{function} will receive one argument for
+% each iteration. The \meta{items} are returned from left to right.
+% 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{sequence} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{sequence}. 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}[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{sequence} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{item} of the \meta{sequence} 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{sequence},
+% or its original value if the \meta{sequence} is empty. The
+% \meta{items} are returned from left to right.
+% \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{sequence} 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{sequence} 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}[EXP, added = 2012-07-13]{\seq_count:N, \seq_count:c}
+% \begin{syntax}
+% \cs{seq_count:N} \meta{sequence}
+% \end{syntax}
+% Leaves the number of items in the \meta{sequence} in the input
+% stream as an \meta{integer denotation}. The total number of items
+% in a \meta{sequence} includes those which are empty and duplicates,
+% \emph{i.e.}~every item in a \meta{sequence} 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{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{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{sequence} \meta{token list variable}
+% \end{syntax}
+% Reads the top item from a \meta{sequence} into the
+% \meta{token list variable} without removing it from the
+% \meta{sequence}. The \meta{token list variable} is assigned locally.
+% If \meta{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{sequence} into the
+% \meta{token list variable}. Both of the variables are assigned
+% locally. If \meta{sequence} 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{sequence} \meta{token list variable}
+% \end{syntax}
+% Pops the top item from a \meta{sequence} into the
+% \meta{token list variable}. The \meta{sequence} is modified globally,
+% while the \meta{token list variable} is assigned locally. If
+% \meta{sequence} 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, stores the top item from a
+% \meta{sequence} in the \meta{token list variable} without removing it from
+% the \meta{sequence}. 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the top item from the
+% \meta{sequence} in the \meta{token list variable}, \emph{i.e.}~removes the
+% item from the \meta{sequence}. Both the \meta{sequence} 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{sequence} \meta{token list variable} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{sequence} 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{sequence} is non-empty, pops the top item from the \meta{sequence}
+% in the \meta{token list variable}, \emph{i.e.}~removes the item from the
+% \meta{sequence}. The \meta{sequence} 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:No, \seq_push:Nx,
+% \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:co, \seq_push:cx,
+% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv,
+% \seq_gpush:No, \seq_gpush:Nx,
+% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv,
+% \seq_gpush:co, \seq_gpush:cx
+% }
+% \begin{syntax}
+% \cs{seq_push:Nn} \meta{sequence} \Arg{item}
+% \end{syntax}
+% Adds the \Arg{item} to the top of the \meta{sequence}.
+% \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{sequence variable} only has distinct items, use
+% \cs{seq_remove_duplicates:N} \meta{sequence variable}. 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
+% \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
+% \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
+% \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
+% \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
+% \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 = 2015-08-01]{\seq_show:N, \seq_show:c}
+% \begin{syntax}
+% \cs{seq_show:N} \meta{sequence}
+% \end{syntax}
+% Displays the entries in the \meta{sequence} in the terminal.
+% \end{function}
+%
+% \begin{function}[added = 2014-08-12, updated = 2015-08-01]{\seq_log:N, \seq_log:c}
+% \begin{syntax}
+% \cs{seq_log:N} \meta{sequence}
+% \end{syntax}
+% Writes the entries in the \meta{sequence} in the log file.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3seq} implementation}
+%
+% \TestFiles{m3seq002,m3seq003}
+%
+% \begin{macrocode}
+%<*initex|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:x}
+% \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{macro}{\@@_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
+ {
+ \__kernel_msg_expandable_error:nn { kernel } { misused-sequence }
+ \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
+ {
+ \tl_set:Nx #1
+ { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_set_from_clist:Nn #1#2
+ {
+ \tl_set:Nx #1
+ { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:NN #1#2
+ {
+ \tl_gset:Nx #1
+ { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2
+ {
+ \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_set_split:Nnn , \seq_set_split:NnV ,
+% \seq_gset_split:Nnn, \seq_gset_split:NnV
+% }
+% \begin{macro}{\@@_set_split:NNnn}
+% \begin{macro}
+% {
+% \@@_set_split_auxi:w, \@@_set_split_auxii: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_auxi:w} \cs{prg_do_nothing:}
+% \meta{item with spaces} \cs{@@_set_split_end:}.
+% Then, \texttt{x}-expansion causes \cs{@@_set_split_auxi:w}
+% to trim spaces, and leaves its result as
+% \cs{@@_set_split_auxii:w} \meta{trimmed item}
+% \cs{@@_set_split_end:}. This is then converted
+% to the \pkg{l3seq} internal structure by another
+% \texttt{x}-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:NNnn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_split:Nnn
+ { \@@_set_split:NNnn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_split:NNnn #1#2#3#4
+ {
+ \tl_if_empty:nTF {#3}
+ {
+ \tl_set:Nn \l_@@_internal_a_tl
+ { \tl_map_function:nN {#4} \@@_wrap_item:n }
+ }
+ {
+ \tl_set:Nn \l_@@_internal_a_tl
+ {
+ \@@_set_split_auxi:w \prg_do_nothing:
+ #4
+ \@@_set_split_end:
+ }
+ \tl_replace_all:Nnn \l_@@_internal_a_tl { #3 }
+ {
+ \@@_set_split_end:
+ \@@_set_split_auxi:w \prg_do_nothing:
+ }
+ \tl_set:Nx \l_@@_internal_a_tl { \l_@@_internal_a_tl }
+ }
+ #1 #2 { \s_@@ \l_@@_internal_a_tl }
+ }
+\cs_new:Npn \@@_set_split_auxi:w #1 \@@_set_split_end:
+ {
+ \exp_not:N \@@_set_split_auxii:w
+ \exp_args:No \tl_trim_spaces:n {#1}
+ \exp_not:N \@@_set_split_end:
+ }
+\cs_new:Npn \@@_set_split_auxii:w #1 \@@_set_split_end:
+ { \@@_wrap_item:n {#1} }
+\cs_generate_variant:Nn \seq_set_split:Nnn { NnV }
+\cs_generate_variant:Nn \seq_gset_split:Nnn { NnV }
+% \end{macrocode}
+% \end{macro}
+% \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:No, \seq_put_left:Nx,
+% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv,
+% \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:No, \seq_gput_left:Nx,
+% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv,
+% \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
+ {
+ \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
+ {
+ \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 , No , Nx }
+\cs_generate_variant:Nn \seq_put_left:Nn { c , cV , cv , co , cx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { NV , Nv , No , Nx }
+\cs_generate_variant:Nn \seq_gput_left:Nn { c , cV , cv , 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:No, \seq_put_right:Nx,
+% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv,
+% \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:No, \seq_gput_right:Nx,
+% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv,
+% \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_gput_right:Nn { NV , Nv , No , Nx }
+\cs_generate_variant:Nn \seq_gput_right:Nn { c , cV , cv , co , cx }
+\cs_generate_variant:Nn \seq_put_right:Nn { NV , Nv , No , Nx }
+\cs_generate_variant:Nn \seq_put_right:Nn { c , cV , cv , 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{x}-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:cn}
+% \UnitTested
+% \begin{macro}{\seq_gremove_all:Nn, \seq_gremove_all:cn}
+% \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{x}-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{x}-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{x}-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 and intermediate scratch list: the lead-off
+% \texttt{x}-type expansion (|#1 #2 {#2}|) ensures that nothing is lost.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_remove_all:Nn
+ { \@@_remove_all_aux:NNn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gremove_all:Nn
+ { \@@_remove_all_aux:NNn \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 { c }
+\cs_generate_variant:Nn \seq_gremove_all:Nn { c }
+% \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 \tl_set:Nx }
+\cs_new_protected:Npn \seq_greverse:N
+ { \@@_reverse:NN \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}[TF]
+% {
+% \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:No, \seq_if_in:Nx,
+% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \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 , No , Nx , c , cV , cv , 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
+ {
+ \tl_set:Nx #2
+ {
+ \exp_after:wN \@@_get_left:wnw
+ #1 \@@_item:n { \q_no_value } \q_stop
+ }
+ }
+\cs_new:Npn \@@_get_left:wnw #1 \@@_item:n #2#3 \q_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 \q_stop #1#2#3 }
+\cs_new_protected:Npn \@@_pop_left:wnwNNN
+ #1 \@@_item:n #2#3 \q_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
+ {
+ \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{x}-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 \tl_set:Nx }
+\cs_new_protected:Npn \seq_gpop_right:NN
+ { \@@_pop:NNNN \@@_pop_right:NNN \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: }
+ \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 \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 \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:cn}
+% \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 \q_stop #1 }
+\cs_new:Npn \@@_item:wNn \s_@@ #1 \q_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 { c }
+% \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 to 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:NNn}
+% 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
+% argument delimited by \cs{@@_item:n} is 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 { } \prg_break_point:
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new:Npn \@@_map_function:Nw #1#2 \@@_item:n #3
+ {
+ #2
+ #1 {#3}
+ \@@_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:x}
+% \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:x
+ {
+ \@@_push_item_def:
+ \cs_gset:Npx \@@_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_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{x}-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:x
+ {
+ \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_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
+ \q_mark { \@@_use:nwwwwnwn {#3} }
+ \q_mark { \@@_use:nwwn {#4} }
+ \q_stop { }
+ }
+ }
+ {
+ \__kernel_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
+ \q_mark #6#7 \q_stop #8
+ {
+ #6 \@@_item:n {#3} \@@_item:n {#4} #5
+ \q_mark {#6} #7 \q_stop { #8 #1 #2 }
+ }
+\cs_new:Npn \@@_use:nwwn #1 \@@_item:n #2 #3 \q_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:No, \seq_push:Nx,
+% \seq_push:cn, \seq_push:cV, \seq_push:cV, \seq_push:co, \seq_push:cx
+% }
+% \UnitTested
+% \begin{macro}{
+% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:No, \seq_gpush:Nx,
+% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \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_new_eq:NN \seq_push:NV \seq_put_left:NV
+\cs_new_eq:NN \seq_push:Nv \seq_put_left:Nv
+\cs_new_eq:NN \seq_push:No \seq_put_left:No
+\cs_new_eq:NN \seq_push:Nx \seq_put_left:Nx
+\cs_new_eq:NN \seq_push:cn \seq_put_left:cn
+\cs_new_eq:NN \seq_push:cV \seq_put_left:cV
+\cs_new_eq:NN \seq_push:cv \seq_put_left:cv
+\cs_new_eq:NN \seq_push:co \seq_put_left:co
+\cs_new_eq:NN \seq_push:cx \seq_put_left:cx
+\cs_new_eq:NN \seq_gpush:Nn \seq_gput_left:Nn
+\cs_new_eq:NN \seq_gpush:NV \seq_gput_left:NV
+\cs_new_eq:NN \seq_gpush:Nv \seq_gput_left:Nv
+\cs_new_eq:NN \seq_gpush:No \seq_gput_left:No
+\cs_new_eq:NN \seq_gpush:Nx \seq_gput_left:Nx
+\cs_new_eq:NN \seq_gpush:cn \seq_gput_left:cn
+\cs_new_eq:NN \seq_gpush:cV \seq_gput_left:cV
+\cs_new_eq:NN \seq_gpush:cv \seq_gput_left:cv
+\cs_new_eq:NN \seq_gpush:co \seq_gput_left:co
+\cs_new_eq:NN \seq_gpush:cx \seq_gput_left: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}
+% \UnitTested
+% Apply the general \cs{msg_show:nnnnnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_show:N { \@@_show:NN \msg_show:nnxxxx }
+\cs_generate_variant:Nn \seq_show:N { c }
+\cs_new_protected:Npn \seq_log:N { \@@_show:NN \msg_log:nnxxxx }
+\cs_generate_variant:Nn \seq_log:N { c }
+\cs_new_protected:Npn \@@_show:NN #1#2
+ {
+ \__kernel_chk_defined:NT #2
+ {
+ #1 { LaTeX/kernel } { show-seq }
+ { \token_to_str:N #2 }
+ { \seq_map_function:NN #2 \msg_show_item:n }
+ { } { }
+ }
+ }
+% \end{macrocode}
+% \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3skip.dtx b/macros/latex/contrib/l3kernel/l3skip.dtx
new file mode 100644
index 0000000000..7f08680583
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3skip.dtx
@@ -0,0 +1,2344 @@
+% \iffalse meta-comment
+%
+%% File: l3skip.dtx
+%
+% Copyright (C) 2004-2011 Frank Mittelbach, The LaTeX3 Project
+% (C) 2012-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Dimensions and skips^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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.
+%
+% \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{dimension expression}
+% \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{dimension expression}.
+% \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{dimension expression}
+% \end{syntax}
+% Adds the result of the \meta{dimension expression} 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{dimension expression}
+% \end{syntax}
+% Sets \meta{dimension} to the value of \meta{dimension expression}, 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{dimension expression}
+% \end{syntax}
+% Subtracts the result of the \meta{dimension expression} 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{dimexpr}
+% \end{syntax}
+% Converts the \meta{dimexpr} 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{dimexpr_1} \Arg{dimexpr_2}
+% \cs{dim_min:nn} \Arg{dimexpr_1} \Arg{dimexpr_2}
+% \end{syntax}
+% Evaluates the two \meta{dimension expressions} 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{dimexpr_1} \Arg{dimexpr_2}
+% \end{syntax}
+% Parses the two \meta{dimension expressions} and converts the ratio of
+% the two to a form suitable for use inside a \meta{dimension expression}.
+% 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:Nx \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{dimexpr_1} \meta{relation} \Arg{dimexpr_2} \\
+% \cs{dim_compare:nNnTF}
+% ~~\Arg{dimexpr_1} \meta{relation} \Arg{dimexpr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the \meta{dimension expressions}
+% 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{dimexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{dimexpr_N} \meta{relation_N} \\
+% ~~~~\meta{dimexpr_{N+1}} \\
+% ~~\} \\
+% \cs{dim_compare:nTF}
+% ~~\{ \\
+% ~~~~\meta{dimexpr_1} \meta{relation_1} \\
+% ~~~~\ldots{} \\
+% ~~~~\meta{dimexpr_N} \meta{relation_N} \\
+% ~~~~\meta{dimexpr_{N+1}} \\
+% ~~\} \\
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{dimension expressions} as
+% described for \cs{dim_eval:n} and compares consecutive result using
+% the corresponding \meta{relation}, namely it compares
+% \meta{dimexpr_1} and \meta{dimexpr_2} using the \meta{relation_1},
+% then \meta{dimexpr_2} and \meta{dimexpr_3} using the
+% \meta{relation_2}, until finally comparing \meta{dimexpr_N} and
+% \meta{dimexpr_{N+1}} using the \meta{relation_N}. The test yields
+% \texttt{true} if all comparisons are \texttt{true}. Each
+% \meta{dimension expression} is evaluated only once, and the
+% evaluation is lazy, in the sense that if one comparison is
+% \texttt{false}, then no other \meta{dimension 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{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 dimension expression} \\
+% ~~|{| \\
+% ~~~~\Arg{dimexpr case_1} \Arg{code case_1} \\
+% ~~~~\Arg{dimexpr case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\Arg{dimexpr case_n} \Arg{code case_n} \\
+% ~~|}| \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% This function evaluates the \meta{test dimension expression} and
+% compares this in turn to each of the
+% \meta{dimension expression 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{dimexpr_1} \meta{relation} \Arg{dimexpr_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{dimension expressions} 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{dimexpr_1} \meta{relation} \Arg{dimexpr_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{dimension expressions} 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{dimexpr_1} \meta{relation} \Arg{dimexpr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{dimension expressions}
+% 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{dimexpr_1} \meta{relation} \Arg{dimexpr_2} \Arg{code}
+% \end{syntax}
+% Evaluates the relationship between the two \meta{dimension expressions}
+% 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{dimension expression}
+% \end{syntax}
+% Evaluates the \meta{dimension expression}, 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]{\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{dimexpr}
+% \end{syntax}
+% Evaluates the \meta{dimension expression}, and leaves the result,
+% expressed in points (\texttt{pt}) in the input stream, with \emph{no
+% units}. The result is rounded by \TeX{} to four or 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, EXP]{\dim_to_decimal_in_bp:n}
+% \begin{syntax}
+% \cs{dim_to_decimal_in_bp:n} \Arg{dimexpr}
+% \end{syntax}
+% Evaluates the \meta{dimension expression}, 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 four or 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.
+% \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{dimexpr}
+% \end{syntax}
+% Evaluates the \meta{dimension expression}, 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, EXP]
+% {\dim_to_decimal_in_unit:nn}
+% \begin{syntax}
+% \cs{dim_to_decimal_in_unit:nn} \Arg{dimexpr_1} \Arg{dimexpr_2}
+% \end{syntax}
+% Evaluates the \meta{dimension expressions}, and leaves the value of
+% \meta{dimexpr_1}, expressed in a unit given by \meta{dimexpr_2}, in
+% the input stream. The result is a decimal number, rounded by \TeX{}
+% to four or 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_unit:nn { 1bp } { 1mm }
+% \end{verbatim}
+% leaves |0.35277| in the input stream, \emph{i.e.}~the magnitude of
+% one big point when converted to millimetres.
+%
+% Note that this function is not optimised for any particular output
+% and as such may give different results to \cs{dim_to_decimal_in_bp:n}
+% or \cs{dim_to_decimal_in_sp:n}. In particular, the latter is able to
+% take a wider range of input values as it is not limited by the ability
+% to calculate a ratio using \eTeX{} primitives, which is required
+% internally by \cs{dim_to_decimal_in_unit:nn}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-05-08, tested = m3fp-convert002]
+% {\dim_to_fp:n}
+% \begin{syntax}
+% \cs{dim_to_fp:n} \Arg{dimexpr}
+% \end{syntax}
+% Expands to an internal floating point number equal to the value of
+% the \meta{dimexpr} 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{dimension expression}
+% \end{syntax}
+% Displays the result of evaluating the \meta{dimension expression}
+% 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{dimension expression}
+% \end{syntax}
+% Writes the result of evaluating the \meta{dimension expression}
+% 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 expression}
+% \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 expression}.
+% \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 expression}
+% \end{syntax}
+% Adds the result of the \meta{skip expression} 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 expression}
+% \end{syntax}
+% Sets \meta{skip} to the value of \meta{skip expression}, 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 expression}
+% \end{syntax}
+% Subtracts the result of the \meta{skip expression} 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{skipexpr_1} \Arg{skipexpr_2}
+% \cs{skip_if_eq:nnTF}
+% ~~\Arg{skipexpr_1} \Arg{skipexpr_2}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% This function first evaluates each of the
+% \meta{skip expressions} 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{skipexpr}
+% \cs{skip_if_finite:nTF} \Arg{skipexpr} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Evaluates the \meta{skip expression} 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 expression}
+% \end{syntax}
+% Evaluates the \meta{skip expression}, 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 expression}
+% \end{syntax}
+% Displays the result of evaluating the \meta{skip expression}
+% 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 expression}
+% \end{syntax}
+% Writes the result of evaluating the \meta{skip expression}
+% 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{skipexpr}
+% \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} renamed.
+% \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{skipexpr}
+% \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} renamed.
+% \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 expression}
+% \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 expression}.
+% \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 expression}
+% \end{syntax}
+% Adds the result of the \meta{muskip expression} 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 expression}
+% \end{syntax}
+% Sets \meta{muskip} to the value of \meta{muskip expression}, 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 expression}
+% \end{syntax}
+% Subtracts the result of the \meta{muskip expression} 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 expression}
+% \end{syntax}
+% Evaluates the \meta{muskip expression}, 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 expression}
+% \end{syntax}
+% Displays the result of evaluating the \meta{muskip expression}
+% 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 expression}
+% \end{syntax}
+% Writes the result of evaluating the \meta{muskip expression}
+% 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}{\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}
+%<*initex|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{Creating and initialising \texttt{dim} variables}
+%
+% \begin{macro}{\dim_new:N, \dim_new:c}
+% Allocating \meta{dim} registers \ldots
+% \begin{macrocode}
+%<*package>
+\cs_new_protected:Npn \dim_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newdimen \cs_end: #1
+ }
+%</package>
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\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).
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \dim_zero:N #1 { #1 \c_zero_skip }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+%
+% Several functions here have a signature |:Nn| and are such that when
+% debugging, the first argument should be checked to be a local/global
+% variable and the second should be wrapped in code for an expression.
+% The temporary function \cs{@@_tmp:w} finds the name |#3| of the
+% function being redefined and writes the appropriate patch.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \__kernel_patch_args:nnnNNpn
+ { #1 ##1 }
+ { }
+ { {##1} { \__kernel_chk_expr:nNnN {##2} \@@_eval:w { } #3 } }
+ #2 #3
+ }
+% \end{macrocode}
+%
+% \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}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \dim_set:Nn #1#2
+ { #1 ~ \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\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 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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 deals with the (incorrect) case |\dimen123|.
+% 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. The debugging code is inserted by
+% \cs{@@_tmp:w}.
+% \begin{macrocode}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \dim_add:Nn #1#2
+ { \tex_advance:D #1 by \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \dim_gadd:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1 by
+ \@@_eval:w #2 \@@_eval_end: \scan_stop:
+ }
+\cs_generate_variant:Nn \dim_add:Nn { c }
+\cs_generate_variant:Nn \dim_gadd:Nn { c }
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \dim_sub:Nn #1#2
+ { \tex_advance:D #1 by - \@@_eval:w #2 \@@_eval_end: \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \dim_gsub:Nn #1#2
+ {
+ \tex_global:D \tex_advance:D #1 by
+ -\@@_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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_abs:n } }
+\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: }
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_max:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \dim_max:nn }
+ }
+\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:
+ }
+\__kernel_patch_args:nNNpn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_min:nn }
+ { \__kernel_chk_expr:nNnN {#2} \@@_eval:w { } \dim_min:nn }
+ }
+\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}
+\__kernel_patch_conditional_args:nNNpnn
+ {
+ { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_compare:nNn }
+ { \@@_eval_end: #2 }
+ { \__kernel_chk_expr:nNnN {#3} \@@_eval:w { } \dim_compare:nNn }
+ }
+\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: } \q_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_q_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 \q_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[index=str_case:nnTF]{str_case:nn(TF)} 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} { } \q_mark {#3} \q_mark {#4} \q_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 \q_mark #4#5 \q_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}
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__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
+ }
+ }
+\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
+ {
+ \__kernel_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:Npx
+ { @@_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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_eval:n } }
+\cs_new:Npn \dim_eval:n #1
+ { \dim_use:N \@@_eval:w #1 \@@_eval_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\dim_use:N, \dim_use:c}
+% Accessing a \meta{dim}.
+% \begin{macrocode}
+\cs_new_eq:NN \dim_use:N \tex_the:D
+% \end{macrocode}
+% We hand-code this for some speed gain:
+% \begin{macrocode}
+%\cs_generate_variant:Nn \dim_use:N { c }
+\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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_to_decimal:n } }
+\cs_new:Npn \dim_to_decimal:n #1
+ {
+ \exp_after:wN
+ \@@_to_decimal:w \dim_use:N \@@_eval:w #1 \@@_eval_end:
+ }
+\use:x
+ {
+ \cs_new:Npn \exp_not:N \@@_to_decimal:w
+ ##1 . ##2 \tl_to_str:n { pt }
+ }
+ {
+ \int_compare:nNnTF {#2} > { 0 }
+ { #1 . #2 }
+ { #1 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_decimal_in_bp:n}
+% Conversion to big points is done using a scaling inside \cs{@@_eval:w}
+% as \eTeX{} does that using $64$-bit precision. Here, $800/803$ is the
+% integer fraction for $72/72.27$. This is a common case so is hand-coded
+% for accuracy (and speed).
+% \begin{macrocode}
+\cs_new:Npn \dim_to_decimal_in_bp:n #1
+ { \dim_to_decimal:n { ( #1 ) * 800 / 803 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_decimal_in_sp:n}
+% Another hard-coded conversion: this one is necessary to avoid things going
+% off-scale.
+% \begin{macrocode}
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \@@_eval:w { }
+ \dim_to_decimal_in_sp:n
+ }
+ }
+\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_unit:nn}
+% An analogue of \cs{dim_ratio:nn} that produces a decimal number as its
+% result, rather than a rational fraction for use within dimension
+% expressions.
+% \begin{macrocode}
+\cs_new:Npn \dim_to_decimal_in_unit:nn #1#2
+ {
+ \dim_to_decimal:n
+ {
+ 1pt *
+ \dim_ratio:nn {#1} {#2}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\dim_to_fp:n}
+% Defined in \pkg{l3fp-convert}, documented here.
+% \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
+ { \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
+ { \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{macro}{\skip_new:N, \skip_new:c}
+% Allocation of a new internal registers.
+% \begin{macrocode}
+%<*package>
+\cs_new_protected:Npn \skip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newskip \cs_end: #1
+ }
+%</package>
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \skip_zero:N #1 { #1 \c_zero_skip }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \skip_gzero:N #1 { \tex_global:D #1 \c_zero_skip }
+\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}
+%
+% Much as for \texttt{dim} variables, \cs{@@_tmp:w} prepares a patch
+% for |:Nn| function definitions in which the first argument should be
+% checked to be a local/global variable and the second should be wrapped
+% in code for an expression.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \__kernel_patch_args:nnnNNpn
+ { #1 ##1 }
+ { }
+ { {##1} { \__kernel_chk_expr:nNnN {##2} \tex_glueexpr:D { } #3 } }
+ #2 #3
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\skip_set:Nn, \skip_set:cn}
+% \begin{macro}{\skip_gset:Nn, \skip_gset:cn}
+% Much the same as for dimensions.
+% \begin{macrocode}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \skip_set:Nn #1#2
+ { #1 ~ \tex_glueexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\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}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \skip_add:Nn #1#2
+ { \tex_advance:D #1 by \tex_glueexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \skip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by \tex_glueexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \skip_add:Nn { c }
+\cs_generate_variant:Nn \skip_gadd:Nn { c }
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \skip_sub:Nn #1#2
+ { \tex_advance:D #1 by - \tex_glueexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \skip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by - \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
+ {
+ \__kernel_patch_conditional_args:nNNpnn
+ {
+ {
+ \__kernel_chk_expr:nNnN
+ {##1} \tex_glueexpr:D { } \skip_if_finite:n
+ }
+ }
+ \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: \q_stop
+ }
+ \cs_new:Npn \@@_if_finite:wwNw ##1 #1 ##2 ; ##3 ##4 \q_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}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \tex_glueexpr:D { } \skip_eval:n } }
+\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 \tex_the:D
+%\cs_generate_variant:Nn \skip_use:N { c }
+\cs_new:Npn \skip_use:c #1 { \tex_the:D \cs:w #1 \cs_end: }
+% \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
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \tex_glueexpr:D { }
+ \skip_horizontal:n
+ }
+ }
+\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
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \tex_glueexpr:D { }
+ \skip_vertical:n
+ }
+ }
+\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
+ { \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
+ { \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}
+%<*package>
+\cs_new_protected:Npn \muskip_new:N #1
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs:w newmuskip \cs_end: #1
+ }
+%</package>
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \muskip_zero:N #1
+ { #1 \c_zero_muskip }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+%
+% See \texttt{skip} case.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \__kernel_patch_args:nnnNNpn
+ { #1 ##1 }
+ { }
+ {
+ {##1}
+ {
+ \__kernel_chk_expr:nNnN {##2}
+ \tex_muexpr:D { \tex_mutoglue:D } #3
+ }
+ }
+ #2 #3
+ }
+% \end{macrocode}
+%
+% \begin{macro}{\muskip_set:Nn, \muskip_set:cn}
+% \begin{macro}{\muskip_gset:Nn, \muskip_gset:cn}
+% This should be pretty familiar.
+% \begin{macrocode}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \muskip_set:Nn #1#2
+ { #1 ~ \tex_muexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\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}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \muskip_set_eq:NN #1#2 { #1 = #2 }
+\cs_generate_variant:Nn \muskip_set_eq:NN { c , Nc , cc }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\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}
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \muskip_add:Nn #1#2
+ { \tex_advance:D #1 by \tex_muexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \muskip_gadd:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by \tex_muexpr:D #2 \scan_stop: }
+\cs_generate_variant:Nn \muskip_add:Nn { c }
+\cs_generate_variant:Nn \muskip_gadd:Nn { c }
+\@@_tmp:w \__kernel_chk_var_local:N
+\cs_new_protected:Npn \muskip_sub:Nn #1#2
+ { \tex_advance:D #1 by - \tex_muexpr:D #2 \scan_stop: }
+\@@_tmp:w \__kernel_chk_var_global:N
+\cs_new_protected:Npn \muskip_gsub:Nn #1#2
+ { \tex_global:D \tex_advance:D #1 by - \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}
+\__kernel_patch_args:nNNpn
+ {
+ {
+ \__kernel_chk_expr:nNnN {#1} \tex_muexpr:D
+ { \tex_mutoglue:D } \muskip_eval:n
+ }
+ }
+\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 \tex_the:D
+\cs_generate_variant:Nn \muskip_use:N { 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
+ { \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
+ { \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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3sort.dtx b/macros/latex/contrib/l3kernel/l3sort.dtx
new file mode 100644
index 0000000000..ddff3a8583
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3sort.dtx
@@ -0,0 +1,1103 @@
+% \iffalse
+%
+%% File l3sort.dtx
+%
+% Copyright (C) 2012-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Sorting functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{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}
+%<*initex|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:x}
+% (or some \cs{exp_args:NNNx}) 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}
+%
+% \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:}. The \LaTeX3 format
+% mode is easiest: no \tn{toks} are ever allocated so available
+% \tn{toks} range from $0$ to \cs{c_max_register_int} and we
+% precompute the result of \cs{@@_shrink_range:}.
+% \begin{macrocode}
+%<*package>
+\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:
+ }
+ }
+ }
+%</package>
+%<*initex>
+\int_const:Nn \c_@@_max_length_int
+ { ( \c_max_register_int + 1 ) * 3 / 4 }
+\cs_new_protected:Npn \@@_compute_range:
+ {
+ \int_set:Nn \l_@@_min_int { 0 }
+ \int_set:Nn \l_@@_true_max_int { \c_max_register_int + 1 }
+ \int_set:Nn \l_@@_max_int { \c_@@_max_length_int }
+ }
+%</initex>
+% \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
+ {
+%<package> \@@_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}
+ \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_gset_from_inline_x:Nnn \g_@@_internal_seq
+ {
+ \int_step_function:nnN
+ { \l_@@_min_int } { \l_@@_top_int - 1 }
+ }
+ { \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
+ \q_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{q_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 \q_mark
+ {
+ #1
+ #2
+ \@@_return_two_error:
+ \@@_return_mark:w
+ \q_mark
+ \@@_return_same:w
+ }
+\cs_new_protected:Npn \sort_return_swapped:
+ #1 \@@_return_mark:w #2 \q_mark
+ {
+ #1
+ #2
+ \@@_return_two_error:
+ \@@_return_mark:w
+ \q_mark
+ \@@_return_swapped:w
+ }
+\cs_new_protected:Npn \@@_return_mark:w #1 \q_mark { }
+\cs_new_protected:Npn \@@_return_none_error:
+ {
+ \__kernel_msg_error:nnxx { kernel } { 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:
+ {
+ \__kernel_msg_error:nnxx { kernel } { 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_recursion_tail \q_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{q_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{q_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{q_mark}\\
+% ~~|{|\\
+% ~~~~\cs{@@_quick_split:NnNn} |#1| \ldots{} \cs{q_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{q_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{q_mark}, namely removing the trailing \cs{s_stop} and
+% \cs{q_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 }
+ \q_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 \q_stop
+ {
+ \@@_quick_split:NnNn #4 \@@_quick_end:nnTFNn { }
+ \q_mark { \@@_quick_cleanup:w \exp_stop_f: }
+ \s_stop \q_stop
+ }
+\cs_new:Npn \@@_quick_cleanup:w #1 \s_stop \q_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{q_mark}.
+% To avoid a memory problem described earlier, all of the ending
+% functions read~|#6| until \cs{q_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 \q_mark #5#6 \q_stop
+ { #5 {#3} #6 \q_stop }
+\cs_new:Npn \@@_quick_only_i_end:nnnwnw #1#2#3#4 \q_mark #5#6 \q_stop
+ {
+ \@@_quick_split:NnNn #1
+ \@@_quick_end:nnTFNn { } \q_mark {#5}
+ {#3}
+ #6 \q_stop
+ }
+\cs_new:Npn \@@_quick_only_ii_end:nnnwnw #1#2#3#4 \q_mark #5#6 \q_stop
+ {
+ \@@_quick_split:NnNn #2
+ \@@_quick_end:nnTFNn { } \q_mark { #5 {#3} }
+ #6 \q_stop
+ }
+\cs_new:Npn \@@_quick_split_end:nnnwnw #1#2#3#4 \q_mark #5#6 \q_stop
+ {
+ \@@_quick_split:NnNn #2 \@@_quick_end:nnTFNn { } \q_mark
+ {
+ \@@_quick_split:NnNn #1
+ \@@_quick_end:nnTFNn { } \q_mark {#5}
+ {#3}
+ }
+ #6 \q_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}
+%<*package>
+\cs_new_protected:Npn \@@_disable_toksdef:
+ { \cs_set_eq:NN \toksdef \@@_disabled_toksdef:n }
+\cs_new_protected:Npn \@@_disabled_toksdef:n #1
+ {
+ \__kernel_msg_error:nnx { kernel } { toksdef }
+ { \token_to_str:N #1 }
+ \@@_error:
+ \tex_toksdef:D #1
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+%</package>
+% \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:
+ \__kernel_msg_error:nnxxx { kernel } { 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:
+ }
+\__kernel_msg_new:nnnn { kernel } { 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}
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+\__kernel_msg_new:nnnn { kernel } { 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.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3str-convert.dtx b/macros/latex/contrib/l3kernel/l3str-convert.dtx
new file mode 100644
index 0000000000..1343955f7a
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3str-convert.dtx
@@ -0,0 +1,3471 @@
+% \iffalse meta-comment
+%
+%% File: l3str-convert.dtx
+%
+% Copyright (C) 2013-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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 \textsf{l3str-convert} package: string encoding conversions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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
+% \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 \\
+% \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{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{Creating $8$-bit mappings}
+%
+% \begin{function}{\str_declare_eight_bit_encoding:nnn}
+% \begin{syntax}
+% \cs{str_declare_eight_bit_encoding:nnn} \Arg{name} \Arg{mapping} \Arg{missing}
+% \end{syntax}
+% 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.
+% \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 |!'()*-./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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=str>
+% \end{macrocode}
+%
+% \subsection{Helpers}
+%
+% \subsubsection{Variables and constants}
+%
+% \begin{macro}{\@@_tmp:w}
+% \begin{variable}{\l_@@_internal_int}
+% \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
+\int_new:N \l_@@_internal_int
+% \end{macrocode}
+% \end{variable}
+% \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}{\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 }
+% \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}{str_byte, str_error}
+% Conversions from one \meta{encoding}/\meta{escaping} pair to another
+% are done within \texttt{x}-expanding assignments. Errors are
+% signalled by raising the relevant flag.
+% \begin{macrocode}
+\flag_new:n { str_byte }
+\flag_new:n { str_error }
+% \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}
+% \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. Spaces with catcode $10$ are ignored.
+% 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 #2
+ #1 { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\prg_new_conditional:Npnn \@@_if_contains_char:nN #1#2 { TF }
+ {
+ \@@_if_contains_char_aux:NN #2 #1 { \prg_break:n { ? \fi: } }
+ \prg_break_point:
+ \prg_return_false:
+ }
+\cs_new:Npn \@@_if_contains_char_aux:NN #1#2
+ {
+ \if_charcode:w #1 #2
+ \exp_after:wN \@@_if_contains_char_true:
+ \fi:
+ \@@_if_contains_char_aux: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:
+ \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:cx { 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
+ {
+ \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
+ {
+ \tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_convert_gmap_internal_loop:Nww
+ \exp_after:wN #1
+ \g_@@_result_tl \s__tl \q_stop \prg_break: \s__tl
+ \prg_break_point:
+ }
+ }
+\cs_new:Npn \@@_convert_gmap_internal_loop:Nww #1 #2 \s__tl #3 \s__tl
+ {
+ \use_none_delimit_by_q_stop:w #3 \q_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:nnx}
+% \begin{macro}{\@@_if_flag_no_error:nnx}
+% 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:nnx} into \cs{@@_if_flag_no_error:nnx}
+% locally.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_flag_error:nnx #1
+ {
+ \flag_if_raised:nTF {#1}
+ { \__kernel_msg_error:nnx { str } }
+ { \use_none:nn }
+ }
+\cs_new_protected:Npn \@@_if_flag_no_error:nnx #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__tl} \meta{Unicode code point} \cs{s__tl}
+% \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:nnx} 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:nnx \@@_if_flag_no_error:nnx }
+ \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:nnx \@@_if_flag_no_error:nnx }
+ \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
+ \tl_gset:Nx \g_@@_result_tl { \__kernel_str_to_other_fast:n {#4} }
+ \exp_after:wN \@@_convert:wwwnn
+ \tl_to_str:n {#5} /// \q_stop
+ { decode } { unescape }
+ \prg_do_nothing:
+ \@@_convert_decode_:
+ \exp_after:wN \@@_convert:wwwnn
+ \tl_to_str:n {#6} /// \q_stop
+ { encode } { escape }
+ \use_ii_i:nn
+ \@@_convert_encode_:
+ \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 \q_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}
+ { \__kernel_msg_error:nnx { 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:Nx \@@_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:
+ \@@_load_catcodes:
+ \file_input:n { l3str-#2- \l_@@_internal_tl .def }
+ \group_end:
+ }
+ {
+ \tl_clear:N \l_@@_internal_tl
+ \__kernel_msg_error:nnxx { 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}
+%
+% \begin{macro}{\@@_load_catcodes:}
+% Since encoding files may be loaded at arbitrary places in a \TeX{}
+% document, including within verbatim mode, we set the catcodes of all
+% characters appearing in any encoding definition file.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_load_catcodes:
+ {
+ \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_parameter:N \#
+ \char_set_catcode_math_superscript:N \^
+ \char_set_catcode_ignore:N \ %
+ \char_set_catcode_space:N \~
+ \tl_map_function:nN { abcdefghijklmnopqrstuvwxyz_:ABCDEFILNPSTUX }
+ \char_set_catcode_letter:N
+ \tl_map_function:nN { 0123456789"'?*+-.(),`!/<>[];= }
+ \char_set_catcode_other:N
+ \char_set_catcode_comment:N \%
+ \int_set:Nn \tex_endlinechar:D {32}
+ }
+% \end{macrocode}
+% \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 \texttt{str_byte}. 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 { str_byte }
+ \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 { str_byte }
+ \tl_gset:Nx \g_@@_result_tl
+ { \exp_args:No \@@_filter_bytes:n \g_@@_result_tl }
+ \@@_if_flag_error:nnx { str_byte } { 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__tl \int_value:w `#1 \s__tl }
+% \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{x}-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 { str_error }
+ \@@_convert_gmap_internal:N \@@_encode_native_char:n
+ \@@_if_flag_error:nnx { str_error }
+ { native-overflow } { }
+ }
+ \cs_new:Npn \@@_encode_native_char:n #1
+ {
+ \if_int_compare:w #1 > \c_@@_max_byte_int
+ \flag_raise:n { str_error }
+ ?
+ \else:
+ \char_generate:nn {#1} {12}
+ \fi:
+ }
+ \__kernel_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
+ \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__tl \int_eval:n {#1} \s__tl }
+% \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
+ \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}
+%
+% This section will be entirely rewritten: it is not yet clear in what
+% situations 8-bit encodings are used, hence I don't know what exactly
+% should be optimized. The current approach is reasonably efficient to
+% convert long strings, and it scales well when using many different
+% encodings. An approach based on csnames would have a smaller constant
+% load time for each individual conversion, but has a large hash table
+% cost. Using a range of \tn{count} registers works for decoding, but
+% not for encoding: one possibility there would be to use a binary tree
+% for the mapping of Unicode characters to bytes, stored as a box, one
+% per encoding.
+%
+% Since the section is going to be rewritten, documentation lacks.
+%
+% All the 8-bit encodings which \pkg{l3str} supports rely on the same
+% internal functions.
+%
+% \begin{macro}{\str_declare_eight_bit_encoding:nnn}
+% All the 8-bit encoding definition file start with
+% \cs{str_declare_eight_bit_encoding:nnn} \Arg{encoding name}
+% \Arg{mapping} \Arg{missing bytes}. 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 the same code
+% point in Unicode.
+% \begin{macrocode}
+\cs_new_protected:Npn \str_declare_eight_bit_encoding:nnn #1#2#3
+ {
+ \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} }
+ \tl_const:cn { c_@@_encoding_#1_tl } {#2}
+ \tl_const:cn { c_@@_encoding_#1_missing_tl } {#3}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_decode_eight_bit:n}
+% \begin{macro}{\@@_decode_eight_bit_load:nn}
+% \begin{macro}{\@@_decode_eight_bit_load_missing:n}
+% \begin{macro}[EXP]{\@@_decode_eight_bit_char:N}
+%^^A todo: document
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_decode_eight_bit:n #1
+ {
+ \group_begin:
+ \int_zero:N \l_@@_internal_int
+ \exp_last_unbraced:Nx \@@_decode_eight_bit_load:nn
+ { \tl_use:c { c_@@_encoding_#1_tl } }
+ { \q_stop \prg_break: } { }
+ \prg_break_point:
+ \exp_last_unbraced:Nx \@@_decode_eight_bit_load_missing:n
+ { \tl_use:c { c_@@_encoding_#1_missing_tl } }
+ { \q_stop \prg_break: }
+ \prg_break_point:
+ \flag_clear:n { str_error }
+ \@@_convert_gmap:N \@@_decode_eight_bit_char:N
+ \@@_if_flag_error:nnx { str_error } { decode-8-bit } {#1}
+ \group_end:
+ }
+\cs_new_protected:Npn \@@_decode_eight_bit_load:nn #1#2
+ {
+ \use_none_delimit_by_q_stop:w #1 \q_stop
+ \tex_dimen:D "#1 = \l_@@_internal_int sp \scan_stop:
+ \tex_skip:D \l_@@_internal_int = "#1 sp \scan_stop:
+ \tex_toks:D \l_@@_internal_int \exp_after:wN { \int_value:w "#2 }
+ \int_incr:N \l_@@_internal_int
+ \@@_decode_eight_bit_load:nn
+ }
+\cs_new_protected:Npn \@@_decode_eight_bit_load_missing:n #1
+ {
+ \use_none_delimit_by_q_stop:w #1 \q_stop
+ \tex_dimen:D "#1 = \l_@@_internal_int sp \scan_stop:
+ \tex_skip:D \l_@@_internal_int = "#1 sp \scan_stop:
+ \tex_toks:D \l_@@_internal_int \exp_after:wN
+ { \int_use:N \c_@@_replacement_char_int }
+ \int_incr:N \l_@@_internal_int
+ \@@_decode_eight_bit_load_missing:n
+ }
+\cs_new:Npn \@@_decode_eight_bit_char:N #1
+ {
+ #1 \s__tl
+ \if_int_compare:w \tex_dimen:D `#1 < \l_@@_internal_int
+ \if_int_compare:w \tex_skip:D \tex_dimen:D `#1 = `#1 \exp_stop_f:
+ \tex_the:D \tex_toks:D \tex_dimen:D
+ \fi:
+ \fi:
+ \int_value:w `#1 \s__tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_convert_encode_eight_bit:n}
+% \begin{macro}{\@@_encode_eight_bit_load:nn}
+% \begin{macro}[rEXP]{\@@_encode_eight_bit_char:n}
+% \begin{macro}[rEXP]{\@@_encode_eight_bit_char_aux:n}
+%^^A todo: document
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_convert_encode_eight_bit:n #1
+ {
+ \group_begin:
+ \int_zero:N \l_@@_internal_int
+ \exp_last_unbraced:Nx \@@_encode_eight_bit_load:nn
+ { \tl_use:c { c_@@_encoding_#1_tl } }
+ { \q_stop \prg_break: } { }
+ \prg_break_point:
+ \flag_clear:n { str_error }
+ \@@_convert_gmap_internal:N \@@_encode_eight_bit_char:n
+ \@@_if_flag_error:nnx { str_error } { encode-8-bit } {#1}
+ \group_end:
+ }
+\cs_new_protected:Npn \@@_encode_eight_bit_load:nn #1#2
+ {
+ \use_none_delimit_by_q_stop:w #1 \q_stop
+ \tex_dimen:D "#2 = \l_@@_internal_int sp \scan_stop:
+ \tex_skip:D \l_@@_internal_int = "#2 sp \scan_stop:
+ \exp_args:NNf \tex_toks:D \l_@@_internal_int
+ { \@@_output_byte:n { "#1 } }
+ \int_incr:N \l_@@_internal_int
+ \@@_encode_eight_bit_load:nn
+ }
+\cs_new:Npn \@@_encode_eight_bit_char:n #1
+ {
+ \if_int_compare:w #1 > \c_max_register_int
+ \flag_raise:n { str_error }
+ \else:
+ \if_int_compare:w \tex_dimen:D #1 < \l_@@_internal_int
+ \if_int_compare:w \tex_skip:D \tex_dimen:D #1 = #1 \exp_stop_f:
+ \tex_the:D \tex_toks:D \tex_dimen:D #1 \exp_stop_f:
+ \exp_after:wN \exp_after:wN \exp_after:wN \use_none:nn
+ \fi:
+ \fi:
+ \@@_encode_eight_bit_char_aux:n {#1}
+ \fi:
+ }
+\cs_new:Npn \@@_encode_eight_bit_char_aux:n #1
+ {
+ \if_int_compare:w #1 > \c_@@_max_byte_int
+ \flag_raise:n { str_error }
+ \else:
+ \@@_output_byte:n {#1}
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \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}
+\__kernel_msg_new:nnn { str } { unknown-esc }
+ { Escaping~scheme~'#1'~(filtered:~'#2')~unknown. }
+\__kernel_msg_new:nnn { str } { unknown-enc }
+ { Encoding~scheme~'#1'~(filtered:~'#2')~unknown. }
+\__kernel_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.
+ }
+\__kernel_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:
+ }
+ {
+ \__kernel_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}
+\__kernel_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'.
+ }
+\__kernel_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 { str_error }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \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:nnx { str_error } { 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 { str_error }
+ \@@_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 { str_error }
+ \@@_unescape_hex_auxii:N
+ }
+ }
+\__kernel_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 { str_byte }
+ \flag_clear:n { str_error }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN #3 \g_@@_result_tl
+ #1 ? { ? \prg_break: }
+ \prg_break_point:
+ }
+ \@@_if_flag_error:nnx { str_byte } { non-byte } { #2 }
+ \@@_if_flag_error:nnx { str_error } { 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 { str_error }
+ * 0 + `#1 \use_i:nn
+ }
+ }
+ {
+ \flag_raise:n { str_error }
+ 0 + `#1 \use_i:nn
+ }
+ \@@_output_end:
+ \use_i:nnn #3 ##2##3
+ }
+ \__kernel_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 { str_byte }
+ \flag_clear:n { str_error }
+ \int_set:Nn \tex_escapechar:D { 92 }
+ \tl_gset:Nx \g_@@_result_tl
+ {
+ \exp_after:wN \@@_unescape_string_newlines:wN
+ \g_@@_result_tl \prg_break: ^^M ?
+ \prg_break_point:
+ }
+ \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:nnx { str_byte } { non-byte } { string }
+ \@@_if_flag_error:nnx { str_error } { 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 { str_error }
+ 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
+ }
+ \__kernel_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:Nx \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 < "21 \exp_stop_f:
+ \prg_return_false:
+ \else:
+ \if_int_compare:w `#1 > "7E \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 < "41 \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 }
+ \q_stop
+ }
+\cs_new:Npn \@@_encode_utf_viii_loop:wwnnw #1; #2; #3#4 #5 \q_stop
+ {
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \@@_output_byte:n { #1 + #4 }
+ \exp_after:wN \use_none_delimit_by_q_stop:w
+ \fi:
+ \exp_after:wN \@@_encode_utf_viii_loop:wwnnw
+ \int_value:w \int_div_truncate:nn {#1} {64} ; #1 ;
+ #5 \q_stop
+ \@@_output_byte:n { #2 - 64 * ( #1 - 2 ) }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \l_@@_missing_flag ,
+% \l_@@_extra_flag ,
+% \l_@@_overlong_flag ,
+% \l_@@_overflow_flag ,
+% }
+% 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 { str_missing }
+\flag_clear_new:n { str_extra }
+\flag_clear_new:n { str_overlong }
+\flag_clear_new:n { str_overflow }
+\__kernel_msg_new:nnnn { str } { utf8-decode }
+ {
+ Invalid~UTF-8~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:nT { str_missing } { ,~missing~continuation~byte }
+ \@@_if_flag_times:nT { str_extra } { ,~extra~continuation~byte }
+ \@@_if_flag_times:nT { str_overlong } { ,~overlong~form }
+ \@@_if_flag_times:nT { str_overflow } { ,~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 { str_missing }
+ {
+ \\\\
+ A~leading~byte~(in~the~range~[192,255])~was~not~followed~by~
+ the~appropriate~number~of~continuation~bytes.
+ }
+ \flag_if_raised:nT { str_extra }
+ {
+ \\\\
+ LaTeX~came~across~a~continuation~byte~when~it~was~not~expected.
+ }
+ \flag_if_raised:nT { str_overlong }
+ {
+ \\\\
+ 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 { str_overflow }
+ {
+ \\\\
+ Unicode~limits~code~points~to~the~range~[0,1114111].
+ }
+ }
+% \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 { str_error }
+ \flag_clear:n { str_missing }
+ \flag_clear:n { str_extra }
+ \flag_clear:n { str_overlong }
+ \flag_clear:n { str_overflow }
+ \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:nnx { str_error } { utf8-decode } { }
+ }
+\cs_new:Npn \@@_decode_utf_viii_start:N #1
+ {
+ #1
+ \if_int_compare:w `#1 < "C0 \exp_stop_f:
+ \s__tl
+ \if_int_compare:w `#1 < "80 \exp_stop_f:
+ \int_value:w `#1
+ \else:
+ \flag_raise:n { str_extra }
+ \flag_raise:n { str_error }
+ \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__tl
+ \use_none_delimit_by_q_stop:w {"80} {"800} {"10000} {"110000} \q_stop
+ \@@_decode_utf_viii_start:N
+ }
+\cs_new:Npn \@@_decode_utf_viii_continuation:wwN
+ #1 \s__tl #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__tl
+ \flag_raise:n { str_missing }
+ \flag_raise:n { str_error }
+ \int_use:N \c_@@_replacement_char_int
+ \fi:
+ \s__tl
+ #2
+ \@@_decode_utf_viii_start:N #3
+ }
+\cs_new:Npn \@@_decode_utf_viii_aux:wNnnwN
+ #1 \s__tl #2#3#4 #5 \@@_decode_utf_viii_start:N #6
+ {
+ \if_int_compare:w #1 < #4 \exp_stop_f:
+ \s__tl
+ \if_int_compare:w #1 < #3 \exp_stop_f:
+ \flag_raise:n { str_overlong }
+ \flag_raise:n { str_error }
+ \int_use:N \c_@@_replacement_char_int
+ \else:
+ #1
+ \fi:
+ \else:
+ \if_meaning:w \q_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__tl
+ #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 { str_overflow }
+ \flag_raise:n { str_error }
+ \int_use:N \c_@@_replacement_char_int
+ }
+\cs_new:Npn \@@_decode_utf_viii_end:
+ {
+ \s__tl
+ \flag_raise:n { str_missing }
+ \flag_raise:n { str_error }
+ \int_use:N \c_@@_replacement_char_int \s__tl
+ \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:Nx \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 { str_error }
+ \cs_set_eq:NN \@@_tmp:w #1
+ \@@_convert_gmap_internal:N \@@_encode_utf_xvi_char:n
+ \@@_if_flag_error:nnx { str_error } { 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 { str_error }
+ \@@_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}
+% {
+% \l_@@_missing_flag ,
+% \l_@@_extra_flag ,
+% \l_@@_end_flag ,
+% }
+% 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 { str_missing }
+ \flag_clear_new:n { str_extra }
+ \flag_clear_new:n { str_end }
+ \__kernel_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.
+ }
+ \__kernel_msg_new:nnnn { str } { utf16-decode }
+ {
+ Invalid~UTF-16~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:nT { str_missing } { ,~missing~trail~surrogate }
+ \@@_if_flag_times:nT { str_extra } { ,~extra~trail~surrogate }
+ \@@_if_flag_times:nT { str_end } { ,~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 { str_missing }
+ {
+ \\\\
+ A~lead~surrogate~was~not~followed~by~a~trail~surrogate.
+ }
+ \flag_if_raised:nT { str_extra }
+ {
+ \\\\
+ LaTeX~came~across~a~trail~surrogate~when~it~was~not~expected.
+ }
+ \flag_if_raised:nT { str_end }
+ {
+ \\\\
+ 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 { str_error }
+ \flag_clear:n { str_missing }
+ \flag_clear:n { str_extra }
+ \flag_clear:n { str_end }
+ \cs_set:Npn \@@_tmp:w ##1 ##2 { ` ## #1 }
+ \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:nnx { str_error } { 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__tl}
+% \meta{code~point} \cs{s__tl}, 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__tl
+ \int_eval:n { "100 * \@@_tmp:w #1#2 + \@@_tmp:w #2#1 } \s__tl
+ \@@_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__tl
+ \int_eval:n
+ {
+ ( "100 * \@@_tmp:w #1#2 + \@@_tmp:w #2#1 - "D7F7 ) * "400
+ + "100 * \@@_tmp:w #4#5 + \@@_tmp:w #5#4
+ }
+ \s__tl
+ \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__tl #3 \s__tl
+ { \@@_decode_utf_xvi_error:nNN { extra } #1#2 }
+ \cs_new:Npn \@@_decode_utf_xvi_error:nNN #1#2#3
+ {
+ \flag_raise:n { str_error }
+ \flag_raise:n { str_#1 }
+ #2 #3 \s__tl
+ \int_use:N \c_@@_replacement_char_int \s__tl
+ }
+% \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:Nx \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}{str_overflow, str_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 { str_overflow }
+ \flag_clear_new:n { str_end }
+ \__kernel_msg_new:nnnn { str } { utf32-decode }
+ {
+ Invalid~UTF-32~string:
+ \exp_last_unbraced:Nf \use_none:n
+ {
+ \@@_if_flag_times:nT { str_overflow } { ,~code~point~too~large }
+ \@@_if_flag_times:nT { str_end } { ,~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 { str_overflow }
+ {
+ \\\\
+ 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 { str_end }
+ {
+ \\\\
+ 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{x}-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__tl} 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 { str_overflow }
+ \flag_clear:n { str_end }
+ \flag_clear:n { str_error }
+ \cs_set:Npn \@@_tmp:w ##1 ##2 { ` ## #1 }
+ \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:nnx { str_error } { 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__tl
+ \if_int_compare:w \@@_tmp:w #1#4 > 0 \exp_stop_f:
+ \flag_raise:n { str_overflow }
+ \flag_raise:n { str_error }
+ \int_use:N \c_@@_replacement_char_int
+ \else:
+ \if_int_compare:w \@@_tmp:w #2#3 > 16 \exp_stop_f:
+ \flag_raise:n { str_overflow }
+ \flag_raise:n { str_error }
+ \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__tl
+ \@@_decode_utf_xxxii_loop:NNNN
+ }
+ \cs_new:Npn \@@_decode_utf_xxxii_end:w #1 \s_stop
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \flag_raise:n { str_end }
+ \flag_raise:n { str_error }
+ #1 \s__tl
+ \int_use:N \c_@@_replacement_char_int \s__tl
+ }
+ \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}
+%
+% \begin{macrocode}
+%</initex|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>
+\str_declare_eight_bit_encoding:nnn { iso88591 }
+ {
+ }
+ {
+ }
+%</iso88591>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso88592>
+\str_declare_eight_bit_encoding:nnn { iso88592 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88593 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88594 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88595 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88596 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88597 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88598 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso88599 }
+ {
+ { D0 } { 011E }
+ { DD } { 0130 }
+ { DE } { 015E }
+ { F0 } { 011F }
+ { FD } { 0131 }
+ { FE } { 015F }
+ }
+ {
+ }
+%</iso88599>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885910>
+\str_declare_eight_bit_encoding:nnn { iso885910 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso885911 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso885913 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso885914 }
+ {
+ { 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>
+\str_declare_eight_bit_encoding:nnn { iso885915 }
+ {
+ { A4 } { 20AC }
+ { A6 } { 0160 }
+ { A8 } { 0161 }
+ { B4 } { 017D }
+ { B8 } { 017E }
+ { BC } { 0152 }
+ { BD } { 0153 }
+ { BE } { 0178 }
+ }
+ {
+ }
+%</iso885915>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*iso885916>
+\str_declare_eight_bit_encoding:nnn { iso885916 }
+ {
+ { 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/macros/latex/contrib/l3kernel/l3str.dtx b/macros/latex/contrib/l3kernel/l3str.dtx
new file mode 100644
index 0000000000..32dd203cfc
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3str.dtx
@@ -0,0 +1,1984 @@
+% \iffalse meta-comment
+%
+%% File: l3str.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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} package: Strings^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{Building strings}
+%
+% \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:Nx,
+% \str_const:cn, \str_const:cV, \str_const:cx
+% }
+% \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}
+% \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}
+%
+% \section{Adding data to string variables}
+%
+% \begin{function}[added = 2015-09-18, updated = 2018-07-28]
+% {
+% \str_set:Nn, \str_set:NV, \str_set:Nx,
+% \str_set:cn, \str_set:cV, \str_set:cx,
+% \str_gset:Nn, \str_gset:NV, \str_gset:Nx,
+% \str_gset:cn, \str_gset:cV, \str_gset:cx
+% }
+% \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:Nx,
+% \str_put_left:cn, \str_put_left:cV, \str_put_left:cx,
+% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Nx,
+% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:cx
+% }
+% \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:Nx,
+% \str_put_right:cn, \str_put_right:cV, \str_put_right:cx,
+% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Nx,
+% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:cx
+% }
+% \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{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 conditionals}
+%
+% \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}
+%
+% \begin{function}[EXP,pTF, added = 2015-09-18]
+% {\str_if_empty:N, \str_if_empty:c}
+% \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.
+% \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}.
+% \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} \meta{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 = 2015-02-28, EXP, noTF]
+% {\str_case:nn, \str_case:on, \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 cases} (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.
+% \end{function}
+%
+% \begin{function}[added = 2018-06-19, EXP, noTF]{\str_case_e:nn}
+% \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 cases}
+% (all token lists are converted to strings). If the two
+% full expansions 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_e:nn}, which does nothing if there is no match, is also
+% available.
+% 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}
+%
+% \section{Mapping to 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:cN}
+% \begin{syntax}
+% \cs{str_map_function:NN} \meta{str~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every \meta{character} in the
+% \meta{str~var} including spaces.
+% See also \cs{str_map_function:nN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14, rEXP]
+% {\str_map_function:nN}
+% \begin{syntax}
+% \cs{str_map_function:nN} \Arg{token list} \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.
+% See also \cs{str_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14]
+% {\str_map_inline:Nn, \str_map_inline:cn}
+% \begin{syntax}
+% \cs{str_map_inline:Nn} \meta{str~var} \Arg{inline function}
+% \end{syntax}
+% 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|. See also \cs{str_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14]
+% {\str_map_inline:nn}
+% \begin{syntax}
+% \cs{str_map_inline:nn} \Arg{token list} \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{string} including spaces.
+% The \meta{inline function} should consist of code which
+% receives the \meta{character} as |#1|. See also \cs{str_map_function:NN}.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-14]
+% {\str_map_variable:NNn, \str_map_variable:cNn}
+% \begin{syntax}
+% \cs{str_map_variable:NNn} \meta{str~var} \meta{variable} \Arg{code}
+% \end{syntax}
+% Stores each \meta{character} of 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-11-14]
+% {\str_map_variable:nNn}
+% \begin{syntax}
+% \cs{str_map_variable:nNn} \Arg{token list} \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:x { \str_range:nnn { abcdef } { 2 } { 5 } }
+% \iow_term:x { \str_range:nnn { abcdef } { -4 } { -1 } }
+% \iow_term:x { \str_range:nnn { abcdef } { -2 } { -1 } }
+% \iow_term:x { \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:x { \str_range:nnn { abcdef } { 5 } { 2 } }
+% \iow_term:x { \str_range:nnn { abcdef } { -1 } { -4 } }
+% \end{verbatim}
+% both yield empty strings.
+%
+% 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:x { \str_range:nnn { abcdefg } { 2 } { 5 } }
+% \iow_term:x { \str_range:nnn { abcdefg } { 2 } { -3 } }
+% \iow_term:x { \str_range:nnn { abcdefg } { -6 } { 5 } }
+% \iow_term:x { \str_range:nnn { abcdefg } { -6 } { -3 } }
+%
+% \iow_term:x { \str_range:nnn { abc~efg } { 2 } { 5 } }
+% \iow_term:x { \str_range:nnn { abc~efg } { 2 } { -3 } }
+% \iow_term:x { \str_range:nnn { abc~efg } { -6 } { 5 } }
+% \iow_term:x { \str_range:nnn { abc~efg } { -6 } { -3 } }
+%
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { 5 } }
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcdefg } { 2 } { -3 } }
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { 5 } }
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcdefg } { -6 } { -3 } }
+%
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { 5 } }
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcd~efg } { 2 } { -3 } }
+% \iow_term:x { \str_range_ignore_spaces:nnn { abcd~efg } { -6 } { 5 } }
+% \iow_term:x { \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|.
+% \end{function}
+%
+% \section{String manipulation}
+%
+% \begin{function}[EXP, added = 2015-03-01]
+% {
+% \str_lower_case:n, \str_lower_case:f,
+% \str_upper_case:n, \str_upper_case:f
+% }
+% \begin{syntax}
+% \cs{str_lower_case:n} \Arg{tokens}
+% \cs{str_upper_case: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_upper_case:f { \tl_head:n {#1} }
+% \str_lower_case: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_fold_case:n} for this
+% situation (case folding is distinct from lower casing).
+% \item Case changing text for typesetting: see the
+% \cs[index=tl_lower_case:n]{tl_lower_case:n(n)},
+% \cs[index=tl_upper_case:n]{tl_upper_case:n(n)} and
+% \cs[index=tl_mixed_case:n]{tl_mixed_case:n(n)} functions which
+% correctly deal with context-dependence and other factors appropriate
+% to text case changing.
+% \end{itemize}
+%
+% \begin{texnote}
+% As with all \pkg{expl3} functions, the input supported by
+% \cs{str_fold_case:n} is \emph{engine-native} characters which are or
+% interoperate with \textsc{utf-8}. As such, when used with \pdfTeX{}
+% \emph{only} the Latin alphabet characters A--Z are case-folded
+% (\emph{i.e.}~the \textsc{ascii} range which coincides with
+% \textsc{utf-8}). Full \textsc{utf-8} support is available with both
+% \XeTeX{} and \LuaTeX{}.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2014-06-19, updated = 2016-03-07]
+% {\str_fold_case:n, \str_fold_case:V}
+% \begin{syntax}
+% \cs{str_fold_case: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_fold_case: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_fold_case: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}).
+%
+% \begin{texnote}
+% As with all \pkg{expl3} functions, the input supported by
+% \cs{str_fold_case:n} is \emph{engine-native} characters which are or
+% interoperate with \textsc{utf-8}. As such, when used with \pdfTeX{}
+% \emph{only} the Latin alphabet characters A--Z are case-folded
+% (\emph{i.e.}~the \textsc{ascii} range which coincides with
+% \textsc{utf-8}). Full \textsc{utf-8} support is available with both
+% \XeTeX{} and \LuaTeX{}, subject only to the fact that \XeTeX{} in
+% particular has issues with characters of code above hexadecimal
+% $0\mathrm{xFFFF}$ when interacting with \cs{tl_to_str:n}.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing strings}
+%
+% \begin{function}[added = 2015-09-18]
+% {\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]
+% {\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 token lists}
+%
+% \begin{variable}[added = 2015-09-19]
+% {
+% \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
+% }
+% Constant strings, containing a single character token, with category
+% code $12$.
+% \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}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=str>
+% \end{macrocode}
+%
+% \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:Nx,
+% \str_set:cn, \str_set:cV, \str_set:cx,
+% \str_gset:Nn, \str_gset:NV, \str_gset:Nx,
+% \str_gset:cn, \str_gset:cV, \str_gset:cx,
+% \str_const:Nn, \str_const:NV, \str_const:Nx,
+% \str_const:cn, \str_const:cV, \str_const:cx,
+% \str_put_left:Nn, \str_put_left:NV, \str_put_left:Nx,
+% \str_put_left:cn, \str_put_left:cV, \str_put_left:cx,
+% \str_gput_left:Nn, \str_gput_left:NV, \str_gput_left:Nx,
+% \str_gput_left:cn, \str_gput_left:cV, \str_gput_left:cx,
+% \str_put_right:Nn, \str_put_right:NV, \str_put_right:Nx,
+% \str_put_right:cn, \str_put_right:cV, \str_put_right:cx,
+% \str_gput_right:Nn, \str_gput_right:NV, \str_gput_right:Nx,
+% \str_gput_right:cn, \str_gput_right:cV, \str_gput_right:cx
+% }
+% Simply convert the token list inputs to \meta{strings}.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_tmp:n #1
+ {
+ \tl_if_blank:nF {#1}
+ {
+ \cs_new_protected:cpx { str_ #1 :Nn } ##1##2
+ {
+ \exp_not:c { tl_ #1 :Nx } ##1
+ { \exp_not:N \tl_to_str:n {##2} }
+ }
+ \cs_generate_variant:cn { str_ #1 :Nn } { NV , Nx , cn , cV , cx }
+ \@@_tmp:n
+ }
+ }
+ \@@_tmp:n
+ { set }
+ { gset }
+ { const }
+ { put_left }
+ { gput_left }
+ { put_right }
+ { gput_right }
+ { }
+\group_end:
+% \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{q_mark} cannot appear in the string to
+% edit so it is used in all cases. Some |x|-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: \tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_once:Nnn
+ { \@@_replace:NNNnn \prg_do_nothing: \tl_gset:Nx }
+\cs_new_protected:Npn \str_replace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \tl_set:Nx }
+\cs_new_protected:Npn \str_greplace_all:Nnn
+ { \@@_replace:NNNnn \@@_replace_next:w \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}
+ {
+ \__kernel_msg_error:nnx { kernel } { empty-search-pattern } {#5}
+ }
+ {
+ \use:x
+ {
+ \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_q_stop:w
+ #5
+ \q_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_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 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_if_eq:nn}
+% \begin{macro}[EXP]{\@@_escape:n}
+% String comparisons rely on the primitive \cs[index=pdfstrcmp]{(pdf)strcmp} if available:
+% \LuaTeX{} does not have it, so emulation is required. As the net result
+% is that we do not \emph{always} use the primitive, the correct approach
+% is to wrap up in a function with defined behaviour. That's done by
+% providing a wrapper and then redefining in the \LuaTeX{} case. Note that
+% the necessary Lua code is loaded in \pkg{l3boostrap}.
+% The need to detokenize and force
+% expansion of input arises from the case where a |#| token is used in the
+% input, \emph{e.g.}~|\__str_if_eq:nn {#} { \tl_to_str:n {#} }|, which
+% otherwise would fail as \cs{tex_luaescapestring:D} does not double
+% such tokens.
+% \begin{macrocode}
+\cs_new:Npn \@@_if_eq:nn #1#2 { \tex_strcmp:D {#1} {#2} }
+\cs_if_exist:NT \tex_luatexversion:D
+ {
+ \cs_set_eq:NN \lua_escape:e \tex_luaescapestring:D
+ \cs_set_eq:NN \lua_now:e \tex_directlua:D
+ \cs_set:Npn \@@_if_eq:nn #1#2
+ {
+ \lua_now:e
+ {
+ l3kernel.strcmp
+ (
+ " \@@_escape:n {#1} " ,
+ " \@@_escape:n {#2} "
+ )
+ }
+ }
+ \cs_new:Npn \@@_escape:n #1
+ {
+ \lua_escape:e
+ { \__kernel_tl_to_str:w \use:e { {#1} } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \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 make life
+% a bit clearer. The \texttt{nn} and \texttt{xx} versions are created
+% directly as this is most efficient.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \str_if_eq:nn #1#2 { p , T , F , TF }
+ {
+ \if_int_compare:w
+ \@@_if_eq:nn { \exp_not:n {#1} } { \exp_not:n {#2} }
+ = 0 \exp_stop_f:
+ \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_int_compare:w \@@_if_eq:nn {#1} {#2} = 0 \exp_stop_f:
+ \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:NN} is different from
+% \cs{tl_if_eq:NN} because it needs to ignore category codes.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F }
+ {
+ \if_int_compare:w
+ \@@_if_eq:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 }
+ = 0 \exp_stop_f: \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:x
+ { \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:x
+ { \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:on, \str_case:nV, \str_case:nv, \str_case_e:nn}
+% \begin{macro}[EXP]{\@@_case:nnTF, \@@_case_e:nnTF}
+% \begin{macro}[EXP]
+% {\@@_case:nw, \@@_case_e:nw, \@@_case_end:nw}
+% Much the same as \cs[index=tl_case:nn]{tl_case:nn(TF)} here:
+% just a change in the internal comparison.
+% \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} { } \q_mark {#3} \q_mark {#4} \q_stop }
+\cs_generate_variant:Nn \str_case:nn { o , nV , nv }
+\prg_generate_conditional_variant:Nnn \str_case:nn
+ { o , nV , nv } { T , F , TF }
+\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} { } \q_mark {#3} \q_mark {#4} \q_stop }
+\cs_new:Npn \@@_case_e:nw #1#2#3
+ {
+ \str_if_eq:eeTF {#1} {#2}
+ { \@@_case_end:nw {#3} }
+ { \@@_case_e:nw {#1} }
+ }
+\cs_new:Npn \@@_case_end:nw #1#2#3 \q_mark #4#5 \q_stop
+ { \exp_end: #1 #4 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Mapping to 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:x
+ {
+ \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
+ {
+ \quark_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:x
+ {
+ \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
+ {
+ \quark_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}
+%
+% \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{q_mark} and \cs{q_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{q_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 ~ \q_mark \q_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 \q_stop
+ {
+ \if_meaning:w A #8
+ \@@_to_other_end:w
+ \fi:
+ \@@_to_other_loop:w
+ #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop
+ }
+ \cs_new:Npn \@@_to_other_end:w \fi: #1 \q_mark #2 * A #3 \q_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 ~ \q_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 \q_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 \q_stop
+ }
+\cs_new:Npn \@@_item:w #1; #2;
+ {
+ \int_compare:nNnTF {#1} < 0
+ {
+ \int_compare:nNnTF {#1} < {-#2}
+ { \use_none_delimit_by_q_stop:w }
+ {
+ \exp_after:wN \use_i_delimit_by_q_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_q_stop:w }
+ {
+ \exp_after:wN \use_i_delimit_by_q_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 \q_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 < 0 \exp_stop_f:
+ \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{q_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 > 0 \exp_stop_f:
+ #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 \q_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 ~
+ \q_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_q_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 }
+ \q_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_q_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_q_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_q_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}
+ { { } } ~ \q_stop
+ }
+\cs_new:Npn \@@_head:w #1 ~ %
+ { \use_i_delimit_by_q_stop:nw #1 { ~ } }
+\cs_new:Npn \str_head_ignore_spaces:n #1
+ {
+ \exp_after:wN \use_i_delimit_by_q_stop:nw
+ \tl_to_str:n {#1} { } \q_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{q_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 \q_stop
+ }
+\cs_new:Npn \@@_tail_auxi:w #1 X #2 \q_stop { \fi: #1 }
+\cs_new:Npn \str_tail_ignore_spaces:n #1
+ {
+ \exp_after:wN \@@_tail_auxii:w
+ \tl_to_str:n {#1} \q_mark \q_mark \q_stop
+ }
+\cs_new:Npn \@@_tail_auxii:w #1 #2 \q_mark #3 \q_stop { #2 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{String manipulation}
+%
+% \begin{macro}[EXP]
+% {
+% \str_fold_case:n, \str_fold_case:V,
+% \str_lower_case:n, \str_lower_case:f,
+% \str_upper_case:n, \str_upper_case: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}
+% 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.
+% \begin{macrocode}
+\cs_new:Npn \str_fold_case:n #1 { \@@_change_case:nn {#1} { fold } }
+\cs_new:Npn \str_lower_case:n #1 { \@@_change_case:nn {#1} { lower } }
+\cs_new:Npn \str_upper_case:n #1 { \@@_change_case:nn {#1} { upper } }
+\cs_generate_variant:Nn \str_fold_case:n { V }
+\cs_generate_variant:Nn \str_lower_case:n { f }
+\cs_generate_variant:Nn \str_upper_case: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
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_end:wn }
+ \@@_change_case_output:fw
+ { \use:c { char_str_ #1 _case:N } #2 }
+ \@@_change_case_loop:nw {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \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
+% }
+% 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:Nx \c_ampersand_str { \cs_to_str:N \& }
+\str_const:Nx \c_atsign_str { \cs_to_str:N \@ }
+\str_const:Nx \c_backslash_str { \cs_to_str:N \\ }
+\str_const:Nx \c_left_brace_str { \cs_to_str:N \{ }
+\str_const:Nx \c_right_brace_str { \cs_to_str:N \} }
+\str_const:Nx \c_circumflex_str { \cs_to_str:N \^ }
+\str_const:Nx \c_colon_str { \cs_to_str:N \: }
+\str_const:Nx \c_dollar_str { \cs_to_str:N \$ }
+\str_const:Nx \c_hash_str { \cs_to_str:N \# }
+\str_const:Nx \c_percent_str { \cs_to_str:N \% }
+\str_const:Nx \c_tilde_str { \cs_to_str:N \~ }
+\str_const:Nx \c_underscore_str { \cs_to_str:N \_ }
+% \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_eq:NN \str_show:N \tl_show:N
+\cs_generate_variant:Nn \str_show:N { c }
+\cs_new_eq:NN \str_log:n \tl_log:n
+\cs_new_eq:NN \str_log:N \tl_log:N
+\cs_generate_variant:Nn \str_log:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3styleguide.pdf b/macros/latex/contrib/l3kernel/l3styleguide.pdf
new file mode 100644
index 0000000000..44fc265426
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3styleguide.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3styleguide.tex b/macros/latex/contrib/l3kernel/l3styleguide.tex
new file mode 100644
index 0000000000..c7221fc0b2
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3styleguide.tex
@@ -0,0 +1,303 @@
+\iffalse meta-comment
+
+File: l3styleguide.tex
+
+Copyright (C) 2011,2012,2014-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 \LaTeX3 Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2019-08-25}
+
+\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{l3docstrip} 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/macros/latex/contrib/l3kernel/l3syntax-changes.pdf b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf
new file mode 100644
index 0000000000..d1b0806fac
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3syntax-changes.tex b/macros/latex/contrib/l3kernel/l3syntax-changes.tex
new file mode 100644
index 0000000000..321657dfb7
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3syntax-changes.tex
@@ -0,0 +1,183 @@
+\iffalse meta-comment
+
+File: l3syntax-changes.tex
+
+Copyright (C) 2011-2012,2017-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 \LaTeX3 Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2019-08-25}
+
+\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 changes
+to functions in \pkg{l3candidates}, 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}
+
+\end{document}
diff --git a/macros/latex/contrib/l3kernel/l3sys.dtx b/macros/latex/contrib/l3kernel/l3sys.dtx
new file mode 100644
index 0000000000..149c577188
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3sys.dtx
@@ -0,0 +1,327 @@
+% \iffalse meta-comment
+%
+%% File: l3sys.dtx
+%
+% Copyright (C) 2015-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package: System/runtime functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{The name of the job}
+%
+% \begin{variable}[added = 2015-09-19]{\c_sys_jobname_str}
+% Constant that gets the \enquote{job name} assigned when \TeX{} starts.
+% \begin{texnote}
+% This copies the contents of the primitive \tn{jobname}. It is a constant
+% that is set by \TeX{} and should not be overwritten by the package.
+% \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 primitives 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}
+%
+% \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: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}
+%
+% \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: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}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3sys} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=sys>
+% \end{macrocode}
+%
+% \subsection{The name of the job}
+%
+% \begin{variable}{\c_sys_jobname_str}
+% Inherited from the \LaTeX3 name for the primitive: this needs to
+% actually contain the text of the job name rather than the name of
+% the primitive, of course.
+% \begin{macrocode}
+%<*initex>
+\tex_everyjob:D \exp_after:wN
+ {
+ \tex_the:D \tex_everyjob:D
+ \str_const:Nx \c_sys_jobname_str { \tex_jobname:D }
+ }
+%</initex>
+%<*package>
+\str_const:Nx \c_sys_jobname_str { \tex_jobname:D }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{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. For
+% \upTeX{}, there is a complexity in that setting |-kanji-internal=sjis|
+% or |-kanji-internal=euc| effective makes it more like \pTeX{}. In those
+% cases we therefore report \pTeX{} rather than \upTeX{}.
+% \begin{macrocode}
+\str_const:Nx \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
+ {
+ \bool_lazy_and:nnTF
+ { \cs_if_exist_p:N \tex_disablecjktoken:D }
+ { \int_compare_p:nNn { \tex_jis:D "2121 } = { "3000 } }
+ { 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}
+%
+% \subsection{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.
+% \begin{macrocode}
+%<*initex>
+\int_const:Nn \c_sys_minute_int
+ { \int_mod:nn { \tex_time:D } { 60 } }
+\int_const:Nn \c_sys_hour_int
+ { \int_div_truncate:nn { \tex_time:D } { 60 } }
+\int_const:Nn \c_sys_day_int { \tex_day:D }
+\int_const:Nn \c_sys_month_int { \tex_month:D }
+\int_const:Nn \c_sys_year_int { \tex_year:D }
+%</initex>
+%<*package>
+\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:
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{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}
+\str_const:Nx \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}
+%
+% \subsection{Randomness}
+%
+% This candidate function is placed there because
+% \cs{sys_if_rand_exist:TF} is used in \pkg{l3fp-rand}.
+%
+% \begin{macro}[EXP, pTF]{\sys_if_rand_exist:}
+% Currently, randomness exists under \pdfTeX{}, \LuaTeX{}, \pTeX{} and \upTeX{}.
+% \begin{macrocode}
+\@@_const:nn { sys_if_rand_exist }
+ { \cs_if_exist_p:N \tex_uniformdeviate:D }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+%\end{implementation}
+%
+%\PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3term-glossary.pdf b/macros/latex/contrib/l3kernel/l3term-glossary.pdf
new file mode 100644
index 0000000000..eab36989e0
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3term-glossary.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3term-glossary.tex b/macros/latex/contrib/l3kernel/l3term-glossary.tex
new file mode 100644
index 0000000000..ac68e5e525
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3term-glossary.tex
@@ -0,0 +1,68 @@
+\iffalse meta-comment
+
+File: l3term-glossary.tex
+
+Copyright (C) 2018-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 \LaTeX3 Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}%
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2019-08-25}
+
+\newcommand{\TF}{\textit{(TF)}}
+
+\begin{document}
+
+\maketitle
+
+This file describes aspects of \TeX{} programming that are relevant in a
+\LaTeX3 context.
+
+\section{Reading a file}
+
+Tokenization.
+
+Treatment of spaces, such as the trap that \verb|\~~a| is equivalent to
+\verb|\~a| in expl syntax, or that \verb|~| fails to give a space at the
+beginning of a line.
+
+\section{Structure of tokens}
+
+Copy there the section ``Description of all possible tokens'' from \texttt{l3token}.
+
+\section{Quantities and expressions}
+
+Integer denotations, dimensions, glue (including \texttt{fill} and \texttt{true pt} and the like).
+
+Syntax of integer expressions (including the trap that \verb|-(1+2)| is invalid).
+
+\section{\LaTeX3 terms}
+
+Terms like ``intexpr'' or ``seq var'' used in syntax blocks.
+
+\end{document}
diff --git a/macros/latex/contrib/l3kernel/l3tl-analysis.dtx b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx
new file mode 100644
index 0000000000..02e08827b4
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx
@@ -0,0 +1,1106 @@
+% \iffalse meta-comment
+%
+%% File: l3tl-analysis.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% 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 \textsf{l3tl-analysis} package: Analysing token lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{\pkg{l3tl-analysis} documentation}
+%
+% This module mostly provides internal functions for use in the
+% \pkg{l3regex} module. However, it provides as a side-effect a user
+% debugging function, very similar to the \cs{ShowTokens} macro from the
+% \pkg{ted} package.
+%
+% \begin{function}[added = 2018-04-09]{\tl_analysis_show:N, \tl_analysis_show:n}
+% \begin{syntax}
+% \cs{tl_analysis_show:n} \Arg{token list}
+% \end{syntax}
+% Displays to the terminal 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]
+% {\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:
+% \begin{itemize}
+% \item \meta{tokens}, which both \texttt{o}-expand and
+% \texttt{x}-expand to the \meta{token}. The detailed form of
+% \meta{token} may change in later releases.
+% \item \meta{char code}, a decimal representation of the character
+% code of the token, $-1$ if it is a control sequence (with
+% \meta{catcode} $0$).
+% \item \meta{catcode}, a capital hexadecimal digit which denotes
+% the category code of the \meta{token} (0: control sequence, 1:
+% begin-group, 2: end-group, 3: math shift, 4: alignment tab, 6:
+% parameter, 7: superscript, 8: subscript, A: space, B: letter,
+% C:other, D:active).
+% \end{itemize}
+% As all other mappings the mapping is done at the current group
+% level, \emph{i.e.}~any local assignments made by the \meta{inline
+% function} remain in effect after the loop.
+% \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{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{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{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}
+%
+% ^^A todo: ask LuaTeX list for an \ifx\undefined <active char>
+% ^^A which does not add the <active char> in memory.
+%
+% \begin{macrocode}
+%<*initex|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{x}-expansion.
+% \begin{macrocode}
+\scan_new:N \s_@@
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_analysis_token}
+% \begin{variable}{\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}
+% \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{x}-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}
+%
+% \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 = -1 \exp_stop_f:
+ \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: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 > 0 \exp_stop_f:
+ \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
+ {
+ \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{x}-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 = 0 \exp_stop_f:
+ \@@_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: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:Nww}
+% 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:Npx \@@_analysis_b_char:Nww #1
+ {
+ \exp_not:N \if_meaning:w #1 \exp_not:N \tex_undefined:D
+ \token_to_str:N D \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_catcode_other_token
+ \token_to_str:N C \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_catcode_letter_token
+ \token_to_str:N B \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_math_toggle_token 3
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_alignment_token 4
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_math_superscript_token 7
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_math_subscript_token 8
+ \exp_not:N \else:
+ \exp_not:N \if_catcode:w #1 \c_space_token
+ \token_to_str:N A \exp_not:N \else:
+ 6
+ \exp_not:n { \fi: \fi: \fi: \fi: \fi: \fi: \fi: \fi: }
+ \exp_not:N \int_value:w `#1 \s_@@
+ \exp_not:N \exp_after:wN \exp_not:N \@@_analysis_b_normals:ww
+ \exp_not:N \int_value:w \exp_not:N \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 = 0 \exp_stop_f:
+ #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{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_inline_aux:Nn}
+% \begin{macro}{\@@_analysis_map_inline_aux:nnn}
+% 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 looping macro, which has a name specific to that nesting
+% depth. That looping 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 performs the user's code
+% |#2|, 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
+ {
+ \@@_analysis:n {#1}
+ \int_gincr:N \g__kernel_prg_map_int
+ \exp_args:Nc \@@_analysis_map_inline_aux:Nn
+ { @@_analysis_map_inline_ \int_use:N \g__kernel_prg_map_int :wNw }
+ }
+\cs_new_protected:Npn \tl_analysis_map_inline:Nn #1
+ { \exp_args:No \tl_analysis_map_inline:nn #1 }
+\cs_new_protected:Npn \@@_analysis_map_inline_aux:Nn #1#2
+ {
+ \cs_gset_protected:Npn #1 ##1 \s_@@ ##2 ##3 \s_@@
+ {
+ \use_none:n ##2
+ \@@_analysis_map_inline_aux:nnn {##1} {##3} {##2}
+ }
+ \cs_gset_protected:Npn \@@_analysis_map_inline_aux:nnn ##1##2##3
+ {
+ #2
+ #1
+ }
+ \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 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Showing the results}
+%
+% \begin{macro}{\tl_analysis_show:N, \tl_analysis_show:n}
+% 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 #1
+ {
+ \tl_if_exist:NTF #1
+ {
+ \exp_args:No \@@_analysis:n {#1}
+ \msg_show:nnxxxx { LaTeX / kernel } { show-tl-analysis }
+ { \token_to_str:N #1 } { \@@_analysis_show: } { } { }
+ }
+ { \tl_show:N #1 }
+ }
+\cs_new_protected:Npn \tl_analysis_show:n #1
+ {
+ \@@_analysis:n {#1}
+ \msg_show:nnxxxx { LaTeX / kernel } { show-tl-analysis }
+ { } { \@@_analysis_show: } { } { }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_analysis_show:, \@@_analysis_show_loop:wNw}
+% Here, |#1| \texttt{o}- and \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 = 0 \exp_stop_f:
+ \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{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:Nx \c_@@_analysis_show_etc_str % (
+ { \token_to_str:N \ETC.) }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { show-tl-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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3tl.dtx b/macros/latex/contrib/l3kernel/l3tl.dtx
new file mode 100644
index 0000000000..77444e6d13
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3tl.dtx
@@ -0,0 +1,3394 @@
+% \iffalse meta-comment
+%
+%% File: l3tl.dtx
+%
+% Copyright (C) 1990-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Token lists^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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:Nx, \tl_const:cn, \tl_const:cx}
+% \begin{syntax}
+% \cs{tl_const:Nn} \meta{tl~var} \Arg{token list}
+% \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{token list}.
+% \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:Nf, \tl_set:Nx,
+% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:cf, \tl_set:cx,
+% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv,
+% \tl_gset:No, \tl_gset:Nf, \tl_gset:Nx,
+% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv,
+% \tl_gset:co, \tl_gset:cf, \tl_gset:cx
+% }
+% \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:No, \tl_put_left:Nx,
+% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:co, \tl_put_left:cx,
+% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:No, \tl_gput_left:Nx,
+% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:co, \tl_gput_left:cx
+% }
+% \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:No, \tl_put_right:Nx,
+% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:co, \tl_put_right:cx,
+% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:No,
+% \tl_gput_right:Nx,
+% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:co,
+% \tl_gput_right:cx
+% }
+% \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{Modifying token list variables}
+%
+% \begin{function}[updated = 2011-08-11]
+% {
+% \tl_replace_once:Nnn, \tl_replace_once:cnn,
+% \tl_greplace_once:Nnn, \tl_greplace_once:cnn
+% }
+% \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:cnn,
+% \tl_greplace_all:Nnn, \tl_greplace_all:cnn
+% }
+% \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:cn,
+% \tl_gremove_once:Nn, \tl_gremove_once:cn
+% }
+% \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}. \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:cn,
+% \tl_gremove_all:Nn, \tl_gremove_all:cn
+% }
+% \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}. \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}
+%
+% \section{Reassigning token list 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:Nno, \tl_set_rescan:Nnx,
+% \tl_set_rescan:cnn, \tl_set_rescan:cno, \tl_set_rescan:cnx,
+% \tl_gset_rescan:Nnn, \tl_gset_rescan:Nno, \tl_gset_rescan:Nnx,
+% \tl_gset_rescan:cnn, \tl_gset_rescan:cno, \tl_gset_rescan:cnx
+% }
+% \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
+% 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}
+% \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
+% 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.
+% \end{texnote}
+% \end{function}
+%
+% \section{Token list conditionals}
+%
+% \begin{function}[EXP,pTF]{\tl_if_blank:n, \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{token list variable} 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}
+% \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 two \meta{token list variables} 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:Nx \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}.
+% \end{function}
+%
+% \begin{function}[TF]{\tl_if_eq:nn}
+% \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.
+% \end{function}
+%
+% \begin{function}[TF]{\tl_if_in:Nn, \tl_if_in:cn}
+% \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:on, \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$).
+% \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} is exactly equal to the special
+% \cs{c_novalue_tl} marker. 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 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 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 \enquote{normal} token.
+% Token groups (|{|\ldots|}|) are not single tokens.
+% \end{function}
+%
+% \begin{function}[added = 2013-07-24, EXP, noTF]{\tl_case:Nn, \tl_case:cn}
+% \begin{syntax}
+% \cs{tl_case:NnTF} \meta{test token list variable} \\
+% ~~"{" \\
+% ~~~~\meta{token list variable case_1} \Arg{code case_1} \\
+% ~~~~\meta{token list variable case_2} \Arg{code case_2} \\
+% ~~~~\ldots \\
+% ~~~~\meta{token list variable case_n} \Arg{code case_n} \\
+% ~~"}" \\
+% ~~\Arg{true code}
+% ~~\Arg{false code}
+% \end{syntax}
+% This function compares the \meta{test token list variable} in turn
+% with each of the \meta{token list variable cases}. If the two
+% are equal (as described for \cs{tl_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{tl_case:Nn}, which does nothing if there is no match, is also
+% available.
+% \end{function}
+%
+% \section{Mapping to 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}[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 variable} 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 the \meta{tokens} are
+% inserted into 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 variable} 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}
+%
+% \section{Using token lists}
+%
+% \begin{function}[EXP]{\tl_to_str:n, \tl_to_str:V}
+% \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).
+% This 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}
+%
+% \section{Working with the content of token lists}
+%
+% \begin{function}[added = 2012-05-13, EXP]
+% {\tl_count:n, \tl_count:V, \tl_count:o}
+% \begin{syntax}
+% \cs{tl_count:n} \Arg{tokens}
+% \end{syntax}
+% Counts the number of \meta{items} in \meta{tokens} 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 \meta{tokens}. 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 token groups 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{tokens}
+% \end{syntax}
+% Counts the number of \TeX{} tokens in the \meta{tokens} 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}
+% \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{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}
+% Reverses the order of the \meta{items} stored in \meta{tl~var}, 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{token list variable}. Braced token groups are copied without
+% reversing the order of tokens, but keep the outer set of braces.
+% See also \cs{tl_reverse:n}, and, for improved performance,
+% \cs{tl_reverse_items:n}.
+% \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} stored in \meta{tl~var},
+% so that \Arg{item_1}\Arg{item_2}\Arg{item_3} \ldots \Arg{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{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: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{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}
+% Removes any leading and trailing explicit space characters
+% (explicit tokens with character code~$32$ and category code~$10$)
+% from the content of the \meta{tl~var}. Note that this therefore
+% \emph{resets} the content of the variable.
+% \end{function}
+%
+% \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{x}-type or \texttt{e}-type argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{The first token from a token list}
+%
+% 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{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{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[updated = 2012-07-09, EXP, pTF]{\tl_if_head_eq_catcode:nN}
+% \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: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}
+% \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 \enquote{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~$12$ 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{Using a single item}
+%
+% \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{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{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 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$.
+%
+% 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:x { \tl_range:nnn { abcd{e{}}fg } { 2 } { 5 } }
+% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { 2 } { -3 } }
+% \iow_term:x { \tl_range:nnn { abcd{e{}}fg } { -6 } { 5 } }
+% \iow_term:x { \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:x { \tl_range:nnn { abcd~{e{}}fg } { 2 } { 5 } }
+% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { 2 } { -3 } }
+% \iow_term:x { \tl_range:nnn { abcd~{e{}}fg } { -6 } { 5 } }
+% \iow_term:x { \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 the third to the last, the call
+% is |\tl_range:nnn { <tl> } { 3 } { -1 }|. Similarly, for discarding
+% the last item, we can do |\tl_range:nnn { <tl> } { 1 } { -2 }|.
+%
+% The behavior of \cs{tl_range:Nnn} is exactly the same, acting on the
+% contents of the tl variable.
+%
+% For improved performance, see \cs{tl_range_braced:nnn} and
+% \cs{tl_range_unbraced:nnn}.
+% \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{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Viewing token lists}
+%
+% \begin{function}[updated = 2015-08-01]{\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}
+% \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 = 2015-08-01]{\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}
+% \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{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:VnTF \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}
+%<*initex|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}{\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:Nx, \tl_const:cn, \tl_const:cx}
+% Constants are also easy to generate.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\cs_new_protected:Npn \tl_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npx #1 { \exp_not:n {#2} }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\cs_new_protected:Npn \tl_const:Nx #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \cs_gset_nopar:Npx #1 {#2}
+ }
+\cs_generate_variant:Nn \tl_const:Nn { c }
+\cs_generate_variant:Nn \tl_const:Nx { c }
+% \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
+ { \tl_set_eq:NN #1 \c_empty_tl }
+\cs_new_protected:Npn \tl_gclear:N #1
+ { \tl_gset_eq:NN #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. When checking
+% is turned on, make sure both variables exist.
+% \begin{macrocode}
+\__kernel_if_debug:TF
+ {
+ \cs_new_protected:Npn \tl_set_eq:NN #1#2
+ {
+ \__kernel_chk_var_local:N #1
+ \__kernel_chk_var_exist:N #2
+ \cs_set_eq:NN #1 #2
+ }
+ \cs_new_protected:Npn \tl_gset_eq:NN #1#2
+ {
+ \__kernel_chk_var_global:N #1
+ \__kernel_chk_var_exist:N #2
+ \cs_gset_eq:NN #1 #2
+ }
+ }
+ {
+ \cs_new_eq:NN \tl_set_eq:NN \cs_set_eq:NN
+ \cs_new_eq:NN \tl_gset_eq:NN \cs_gset_eq:NN
+ }
+\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}
+\__kernel_patch:nnNNpn
+ {
+ \__kernel_chk_var_exist:N #2
+ \__kernel_chk_var_exist:N #3
+ }
+ { }
+\cs_new_protected:Npn \tl_concat:NNN #1#2#3
+ { \tl_set:Nx #1 { \exp_not:o {#2} \exp_not:o {#3} } }
+\__kernel_patch:nnNNpn
+ {
+ \__kernel_chk_var_exist:N #2
+ \__kernel_chk_var_exist:N #3
+ }
+ { }
+\cs_new_protected:Npn \tl_gconcat:NNN #1#2#3
+ { \tl_gset:Nx #1 { \exp_not:o {#2} \exp_not:o {#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_lccode:D `A = `-
+\tex_lccode:D `N = `N
+\tex_lccode:D `V = `V
+\tex_lowercase:D
+ {
+ \group_end:
+ \tl_const:Nn \c_novalue_tl { ANoValue- }
+ }
+% \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:Nf, \tl_set:Nx,
+% \tl_set:cn, \tl_set:cV, \tl_set:cv, \tl_set:co, \tl_set:cf, \tl_set:cx
+% }
+% \begin{macro}
+% {
+% \tl_gset:Nn, \tl_gset:NV, \tl_gset:Nv,
+% \tl_gset:No, \tl_gset:Nf, \tl_gset:Nx,
+% \tl_gset:cn, \tl_gset:cV, \tl_gset:cv,
+% \tl_gset:co, \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
+% \enquote{by hand} as it is used quite a lot. Each definition is
+% prefixed by a call to \cs{__kernel_patch:nnNNpn} which adds an
+% existence check to the definition.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_set:Nn #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:n {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_set:No #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_set:Nx #1#2
+ { \cs_set_nopar:Npx #1 {#2} }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gset:Nn #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gset:No #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gset:Nx #1#2
+ { \cs_gset_nopar:Npx #1 {#2} }
+\cs_generate_variant:Nn \tl_set:Nn { NV , Nv , Nf }
+\cs_generate_variant:Nn \tl_set:Nx { c }
+\cs_generate_variant:Nn \tl_set:Nn { c, co , cV , cv , cf }
+\cs_generate_variant:Nn \tl_gset:Nn { NV , Nv , Nf }
+\cs_generate_variant:Nn \tl_gset:Nx { c }
+\cs_generate_variant:Nn \tl_gset:Nn { c, co , cV , cv , cf }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_put_left:Nn, \tl_put_left:NV, \tl_put_left:No, \tl_put_left:Nx,
+% \tl_put_left:cn, \tl_put_left:cV, \tl_put_left:co, \tl_put_left:cx
+% }
+% \begin{macro}
+% {
+% \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:No, \tl_gput_left:Nx,
+% \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:co, \tl_gput_left:cx
+% }
+% Adding to the left is done directly to gain a little performance.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_left:Nn #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_left:NV #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_left:No #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_left:Nx #1#2
+ { \cs_set_nopar:Npx #1 { #2 \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_left:Nn #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:n {#2} \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_left:NV #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:V #2 \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_left:No #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o {#2} \exp_not:o #1 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_left:Nx #1#2
+ { \cs_gset_nopar:Npx #1 { #2 \exp_not:o {#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:No { c }
+\cs_generate_variant:Nn \tl_put_left:Nx { c }
+\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:No { c }
+\cs_generate_variant:Nn \tl_gput_left:Nx { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_put_right:Nn, \tl_put_right:NV, \tl_put_right:No, \tl_put_right:Nx,
+% \tl_put_right:cn, \tl_put_right:cV, \tl_put_right:co, \tl_put_right:cx
+% }
+% \begin{macro}
+% {
+% \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:No,
+% \tl_gput_right:Nx,
+% \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:co,
+% \tl_gput_right:cx
+% }
+% The same on the right.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_right:Nn #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_right:NV #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_right:No #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_put_right:Nx #1#2
+ { \cs_set_nopar:Npx #1 { \exp_not:o #1 #2 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_right:Nn #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:n {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_right:NV #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:V #2 } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_right:No #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o #1 \exp_not:o {#2} } }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_gput_right:Nx #1#2
+ { \cs_gset_nopar:Npx #1 { \exp_not:o {#1} #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:No { c }
+\cs_generate_variant:Nn \tl_put_right:Nx { c }
+\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:No { c }
+\cs_generate_variant:Nn \tl_gput_right:Nx { c }
+% \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:Nx \c_@@_rescan_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \tl_set_rescan:Nnn, \tl_set_rescan:Nno, \tl_set_rescan:Nnx,
+% \tl_set_rescan:cnn, \tl_set_rescan:cno, \tl_set_rescan:cnx
+% }
+% \begin{macro}
+% {
+% \tl_gset_rescan:Nnn, \tl_gset_rescan:Nno, \tl_gset_rescan:Nnx,
+% \tl_gset_rescan:cnn, \tl_gset_rescan:cno, \tl_gset_rescan:cnx
+% }
+% \begin{macro}{\tl_rescan:nn}
+% \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 \tl_clear:N \exp_after:wN \l_@@_internal_a_tl
+ \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
+ {
+ \exp_args:No \tex_everyeof:D { \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 { Nno , Nnx }
+\cs_generate_variant:Nn \tl_set_rescan:Nnn { c , cno , cnx }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nno , Nnx }
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { c , cno }
+% \end{macrocode}
+% \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{q_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 }
+ \q_stop
+ }
+ \cs_set:Npn \@@_rescan:NNw ##1##2##3 #2 #1 ##4 ##5 \q_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_all:Nnn, \tl_replace_all:cnn}
+% \begin{macro}{\tl_greplace_all:Nnn, \tl_greplace_all:cnn}
+% \begin{macro}{\tl_replace_once:Nnn, \tl_replace_once:cnn}
+% \begin{macro}{\tl_greplace_once:Nnn, \tl_greplace_once:cnn}
+% 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{x}-type
+% assignment function \cs{tl_set:Nx} or \cs{tl_gset:Nx} 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 \tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_once:Nnn
+ { \@@_replace:NnNNNnn \q_mark ? \@@_replace_wrap:w \tl_gset:Nx }
+\cs_new_protected:Npn \tl_replace_all:Nnn
+ { \@@_replace:NnNNNnn \q_mark ? \@@_replace_next:w \tl_set:Nx }
+\cs_new_protected:Npn \tl_greplace_all:Nnn
+ { \@@_replace:NnNNNnn \q_mark ? \@@_replace_next:w \tl_gset:Nx }
+\cs_generate_variant:Nn \tl_replace_once:Nnn { c }
+\cs_generate_variant:Nn \tl_greplace_once:Nnn { c }
+\cs_generate_variant:Nn \tl_replace_all:Nnn { c }
+\cs_generate_variant:Nn \tl_greplace_all:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_replace:NnNNNnn,
+% \@@_replace_auxi:NnnNNNnn,
+% \@@_replace_auxii:nNNNnn,
+% \@@_replace_next: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}
+ {
+ \__kernel_msg_error:nnx { 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{x}-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{x}-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
+ { \exp_not:o { \use_none:nn ##1 } ##2 }
+ \cs_set:Npx \@@_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:w
+ \exp_after:wN { \exp_after:wN }
+ \exp_after:wN { \exp_after:wN }
+ #4
+ #1
+ {
+ \if_false: { \fi: }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+ #5
+ }
+ \group_align_safe_end:
+ }
+\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:cn}
+% \begin{macro}{\tl_gremove_once:Nn, \tl_gremove_once:cn}
+% 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 { c }
+\cs_generate_variant:Nn \tl_gremove_once:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_remove_all:Nn, \tl_remove_all:cn}
+% \begin{macro}{\tl_gremove_all:Nn, \tl_gremove_all:cn}
+% 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 { c }
+\cs_generate_variant:Nn \tl_gremove_all:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Token list conditionals}
+%
+% \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_meaning:w} \cs{q_nil} |...| \cs{q_nil}.
+% \begin{macrocode}
+\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
+ { V , o } { p , T , F , TF }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \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}
+% Convert the argument to a string: this is empty if and only if
+% the argument is. Then |\if_meaning:w \q_nil ... \q_nil| is
+% \texttt{true} if and only if the string |...| is empty.
+% It could be tempting to use |\if_meaning:w \q_nil #1 \q_nil| directly.
+% This fails on a token
+% list starting with \cs{q_nil} of course but more troubling is the
+% case where argument is a complete conditional such as \cs{if_true:}
+% a \cs{else:} b \cs{fi:} because then \cs{if_true:} is used by
+% \cs{if_meaning:w}, the test turns out \texttt{false}, the \cs{else:}
+% executes the \texttt{false} branch, the \cs{fi:} ends it and the
+% \cs{q_nil} at the end
+% starts executing\dots{}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_empty:n #1 { p , TF , T , F }
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
+ \tl_to_str:n {#1} \q_nil
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_empty:n
+ { V } { 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.
+% \begin{macrocode}
+\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
+ }
+\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_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_conditional:Npnn \tl_if_eq:NN #1#2 { p , T , F , TF }
+ {
+ \if_meaning:w #1 #2
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_eq:NN
+ { Nc , c , cc } { p , TF , T , F }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\tl_if_eq:nn}
+% \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl}
+% 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:
+ }
+\tl_new:N \l_@@_internal_a_tl
+\tl_new:N \l_@@_internal_b_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[TF]{\tl_if_in:Nn, \tl_if_in:cn}
+% 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
+ { c } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[TF]{\tl_if_in:nn, \tl_if_in:Vn, \tl_if_in:on, \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:nn} 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 , o , no } { T , F , TF }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF, EXP]{\tl_if_novalue:n}
+% \begin{macro}[EXP]{\@@_if_novalue:w}
+% Tests for |-NoValue-|: this is similar to \cs{tl_if_in:nn} but set
+% up to be expandable and to check the value exactly. The question
+% mark prevents the auxiliary from losing braces.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \prg_new_conditional:Npnn \tl_if_novalue:n ##1
+ { p , T , F , TF }
+ {
+ \str_if_eq:onTF
+ { \@@_if_novalue:w ? ##1 { } #1 }
+ { ? { } #1 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ \cs_new:Npn \@@_if_novalue:w ##1 #1 {##1}
+ }
+\exp_args:No \@@_tmp:w { \c_novalue_tl }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:N}
+% Expand the token list and feed it to \cs{tl_if_single:n}.
+% \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 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single:n}
+% \begin{macro}[EXP,pTF]{\@@_if_single:n}
+% 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{tl_to_str:n} 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 catcode test yields \texttt{false}. If
+% |#1| has a single item, the token is~|^| and the catcode test
+% yields \texttt{true}. Otherwise, it is one of the characters
+% resulting from \cs{tl_to_str:n}, and the catcode test yields
+% \texttt{false}. Note that \cs{if_catcode: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_catcode:w ^ \exp_after:wN \@@_if_single:nnw
+ \__kernel_tl_to_str:w
+ \exp_after:wN { \use_none:nn #1 ?? } ^ ? \q_stop
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_single:nnw #1#2#3 \q_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}
+%
+% \begin{macro}[EXP, noTF]{\tl_case:Nn, \tl_case:cn}
+% \begin{macro}[EXP]{\@@_case:nnTF}
+% \begin{macro}[EXP]{\@@_case:Nw}
+% \begin{macro}[EXP]{\@@_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 \tl_case:Nn #1#2
+ {
+ \exp:w
+ \@@_case:NnTF #1 {#2} { } { }
+ }
+\cs_new:Npn \tl_case:NnT #1#2#3
+ {
+ \exp:w
+ \@@_case:NnTF #1 {#2} {#3} { }
+ }
+\cs_new:Npn \tl_case:NnF #1#2#3
+ {
+ \exp:w
+ \@@_case:NnTF #1 {#2} { } {#3}
+ }
+\cs_new:Npn \tl_case:NnTF #1#2
+ {
+ \exp:w
+ \@@_case:NnTF #1 {#2}
+ }
+\cs_new:Npn \@@_case:NnTF #1#2#3#4
+ { \@@_case:Nw #1 #2 #1 { } \q_mark {#3} \q_mark {#4} \q_stop }
+\cs_new:Npn \@@_case:Nw #1#2#3
+ {
+ \tl_if_eq:NNTF #1 #2
+ { \@@_case_end:nw {#3} }
+ { \@@_case:Nw #1 }
+ }
+\cs_generate_variant:Nn \tl_case:Nn { c }
+\prg_generate_conditional_variant:Nnn \tl_case:Nn
+ { c } { T , F , TF }
+% \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{q_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{q_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 \q_mark #4#5 \q_stop
+ { \exp_end: #1 #4 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Mapping to token lists}
+%
+% \begin{macro}{\tl_map_function:nN}
+% \begin{macro}{\tl_map_function:NN, \tl_map_function:cN}
+% \begin{macro}{\@@_map_function:Nn}
+% Expandable loop macro for token lists. These have the advantage of not
+% needing to test if the argument is empty, because if it is, the stop
+% marker is read immediately and the loop terminated.
+% \begin{macrocode}
+\cs_new:Npn \tl_map_function:nN #1#2
+ {
+ \@@_map_function:Nn #2 #1
+ \q_recursion_tail
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new:Npn \tl_map_function:NN
+ { \exp_args:No \tl_map_function:nN }
+\cs_new:Npn \@@_map_function:Nn #1#2
+ {
+ \quark_if_recursion_tail_break:nN {#2} \tl_map_break:
+ #1 {#2} \@@_map_function:Nn #1
+ }
+\cs_generate_variant:Nn \tl_map_function:NN { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \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:Nn}
+% 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:Nn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ #1 \q_recursion_tail
+ \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_variable:nNn}
+% \begin{macro}{\tl_map_variable:NNn, \tl_map_variable:cNn}
+% \begin{macro}{\@@_map_variable:Nnn}
+% \cs{tl_map_variable:nNn} \meta{token list} \meta{tl~var}
+% \meta{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
+ {
+ \@@_map_variable:Nnn #2 {#3} #1
+ \q_recursion_tail
+ \prg_break_point:Nn \tl_map_break: { }
+ }
+\cs_new_protected:Npn \tl_map_variable:NNn
+ { \exp_args:No \tl_map_variable:nNn }
+\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+ {
+ \quark_if_recursion_tail_break:nN {#3} \tl_map_break:
+ \tl_set:Nn #1 {#3}
+ \use:n {#2}
+ \@@_map_variable:Nnn #1 {#2}
+ }
+\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:V}
+% Another name for a primitive: defined in \pkg{l3basics}.
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_to_str:n { V }
+% \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}
+ {
+ \__kernel_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: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 , 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:NNNnn
+ \@@_act_count_normal:nN
+ \@@_act_count_group:nn
+ \@@_act_count_space:n
+ { }
+ {#1}
+ }
+ }
+\cs_new:Npn \@@_act_count_normal:nN #1 #2 { 1 + }
+\cs_new:Npn \@@_act_count_space:n #1 { 1 + }
+\cs_new:Npn \@@_act_count_group:nn #1 #2
+ { 2 + \tl_count_tokens:n {#2} + }
+% \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{q_stop}.
+% \begin{macrocode}
+\cs_new:Npn \tl_reverse_items:n #1
+ {
+ \@@_reverse_items:nwNwn #1 ?
+ \q_mark \@@_reverse_items:nwNwn
+ \q_mark \@@_reverse_items:wn
+ \q_stop { }
+ }
+\cs_new:Npn \@@_reverse_items:nwNwn #1 #2 \q_mark #3 #4 \q_stop #5
+ {
+ #3 #2
+ \q_mark \@@_reverse_items:nwNwn
+ \q_mark \@@_reverse_items:wn
+ \q_stop { {#1} #5 }
+ }
+\cs_new:Npn \@@_reverse_items:wn #1 \q_stop #2
+ { \exp_not:o { \use_none:nn #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_trim_spaces:n, \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{q_mark}, and whose second argument is a
+% \meta{continuation}, which receives as a braced argument
+% \cs{use_none:n} \cs{q_mark} \meta{trimmed token list}. In the case
+% at hand, we take \cs{exp_not:o} as our continuation, so that space
+% trimming behaves correctly within an \texttt{x}-type expansion.
+% \begin{macrocode}
+\cs_new:Npn \tl_trim_spaces:n #1
+ { \@@_trim_spaces:nn { \q_mark #1 } \exp_not:o }
+\cs_generate_variant:Nn \tl_trim_spaces:n { o }
+\cs_new:Npn \tl_trim_spaces_apply:nN #1#2
+ { \@@_trim_spaces:nn { \q_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
+ { \tl_set:Nx #1 { \exp_args:No \tl_trim_spaces:n {#1} } }
+\cs_new_protected:Npn \tl_gtrim_spaces:N #1
+ { \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
+% }
+% 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{q_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{q_nil}
+% matches the one present in the definition of \cs{tl_trim_spacs:n}.
+% Then \cs{@@_trim_spaces_auxiv:w} puts the token list into a group,
+% with \cs{use_none:n} placed there to gobble a lingering \cs{q_mark},
+% and feeds this to the \meta{continuation}.
+% \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn \@@_trim_spaces:nn ##1
+ {
+ \@@_trim_spaces_auxi:w
+ ##1
+ \q_nil
+ \q_mark #1 { }
+ \q_mark \@@_trim_spaces_auxii:w
+ \@@_trim_spaces_auxiii:w
+ #1 \q_nil
+ \@@_trim_spaces_auxiv:w
+ \q_stop
+ }
+ \cs_new:Npn \@@_trim_spaces_auxi:w ##1 \q_mark #1 ##2 \q_mark ##3
+ {
+ ##3
+ \@@_trim_spaces_auxi:w
+ \q_mark
+ ##2
+ \q_mark #1 {##1}
+ }
+ \cs_new:Npn \@@_trim_spaces_auxii:w
+ \@@_trim_spaces_auxi:w \q_mark \q_mark ##1
+ {
+ \@@_trim_spaces_auxiii:w
+ ##1
+ }
+ \cs_new:Npn \@@_trim_spaces_auxiii:w ##1 #1 \q_nil ##2
+ {
+ ##2
+ ##1 \q_nil
+ \@@_trim_spaces_auxiii:w
+ }
+ \cs_new:Npn \@@_trim_spaces_auxiv:w ##1 \q_nil ##2 \q_stop ##3
+ { ##3 { \use_none:n ##1 } }
+ }
+\@@_tmp:w { ~ }
+% \end{macrocode}
+% \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{Token by token changes}
+%
+% \begin{variable}{\q_@@_act_mark, \q_@@_act_stop}
+% The \cs[no-index]{@@_act_\ldots{}} functions may be applied to any token list.
+% Hence, we use two private quarks, to allow any token, even quarks,
+% in the token list.
+% Only \cs{q_@@_act_mark} and \cs{q_@@_act_stop} may not appear
+% in the token lists manipulated by \cs{@@_act:NNNnn} functions.
+% No quark module yet, so do things by hand.
+% \begin{macrocode}
+\cs_new_nopar:Npn \q_@@_act_mark { \q_@@_act_mark }
+\cs_new_nopar:Npn \q_@@_act_stop { \q_@@_act_stop }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_act:NNNnn}
+% \begin{macro}[EXP]{\@@_act_output:n, \@@_act_reverse_output:n}
+% \begin{macro}[EXP]{\@@_act_loop:w}
+% \begin{macro}[EXP]{\@@_act_normal:NwnNNN}
+% \begin{macro}[EXP]{\@@_act_group:nwnNNN}
+% \begin{macro}[EXP]{\@@_act_space:wwnNNN}
+% \begin{macro}[EXP]{\@@_act_end:w}
+% To help control the expansion, \cs{@@_act:NNNnn} should always
+% be proceeded by \cs{exp:w} and ends by producing \cs{exp_end:}
+% once the result has been obtained. Then loop over tokens,
+% groups, and spaces in |#5|. The marker \cs{q_@@_act_mark}
+% is used both to avoid losing outer braces and to detect the
+% end of the token list more easily. The result is stored
+% as an argument for the dummy function \cs{@@_act_result:n}.
+% \begin{macrocode}
+\cs_new:Npn \@@_act:NNNnn #1#2#3#4#5
+ {
+ \group_align_safe_begin:
+ \@@_act_loop:w #5 \q_@@_act_mark \q_@@_act_stop
+ {#4} #1 #2 #3
+ \@@_act_result:n { }
+ }
+% \end{macrocode}
+% In the loop, we check how the token list begins and act
+% accordingly. In the \enquote{normal} case, we may have
+% reached \cs{q_@@_act_mark}, 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 a
+% group or with a space. Some extra work is needed to
+% make \cs{@@_act_space:wwnNNN} gobble the space.
+% \begin{macrocode}
+\cs_new:Npn \@@_act_loop:w #1 \q_@@_act_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_act_normal:NwnNNN }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_act_group:nwnNNN }
+ { \@@_act_space:wwnNNN }
+ }
+ #1 \q_@@_act_stop
+ }
+\cs_new:Npn \@@_act_normal:NwnNNN #1 #2 \q_@@_act_stop #3#4
+ {
+ \if_meaning:w \q_@@_act_mark #1
+ \exp_after:wN \@@_act_end:wn
+ \fi:
+ #4 {#3} #1
+ \@@_act_loop:w #2 \q_@@_act_stop
+ {#3} #4
+ }
+\cs_new:Npn \@@_act_end:wn #1 \@@_act_result:n #2
+ { \group_align_safe_end: \exp_end: #2 }
+\cs_new:Npn \@@_act_group:nwnNNN #1 #2 \q_@@_act_stop #3#4#5
+ {
+ #5 {#3} {#1}
+ \@@_act_loop:w #2 \q_@@_act_stop
+ {#3} #4 #5
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \@@_act_space:wwnNNN \c_space_tl #1 \q_@@_act_stop #2#3#4#5
+ {
+ #5 {#2}
+ \@@_act_loop:w #1 \q_@@_act_stop
+ {#2} #3 #4 #5
+ }
+% \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}
+%
+% \begin{macro}[EXP]{\tl_reverse:n, \tl_reverse:o, \tl_reverse:V}
+% \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:NNNnn}.
+% 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. All of the internal functions here drop one argument:
+% this is needed by \cs{@@_act:NNNnn} when changing case (to record
+% which direction the change is in), but not when reversing the tokens.
+% \begin{macrocode}
+\cs_new:Npn \tl_reverse:n #1
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \@@_act:NNNnn
+ \@@_reverse_normal:nN
+ \@@_reverse_group_preserve:nn
+ \@@_reverse_space:n
+ { }
+ {#1}
+ }
+ }
+\cs_generate_variant:Nn \tl_reverse:n { o , V }
+\cs_new:Npn \@@_reverse_normal:nN #1#2
+ { \@@_act_reverse_output:n {#2} }
+\cs_new:Npn \@@_reverse_group_preserve:nn #1#2
+ { \@@_act_reverse_output:n { {#2} } }
+\cs_new:Npn \@@_reverse_space:n #1
+ { \@@_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
+ { \tl_set:Nx #1 { \exp_args:No \tl_reverse:n { #1 } } }
+\cs_new_protected:Npn \tl_greverse:N #1
+ { \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{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}
+% \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 to 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. Using a marker, we can see if
+% what we are grabbing is exactly the marker, or there is anything else to
+% deal with. Is there is, there is a loop. If not, tidy up and leave the
+% item in the output stream. More detail in
+% \url{http://tex.stackexchange.com/a/70168}.
+% \begin{macrocode}
+\cs_new:Npn \tl_head:n #1
+ {
+ \__kernel_exp_not:w
+ \if_false: { \fi: \@@_head_auxi:nw #1 { } \q_stop }
+ }
+\cs_new:Npn \@@_head_auxi:nw #1#2 \q_stop
+ {
+ \exp_after:wN \@@_head_auxii:n \exp_after:wN {
+ \if_false: } \fi: {#1}
+ }
+\cs_new:Npn \@@_head_auxii:n #1
+ {
+ \exp_after:wN \if_meaning:w \exp_after:wN \q_nil
+ \__kernel_tl_to_str:w \exp_after:wN { \use_none:n #1 } \q_nil
+ \exp_after:wN \use_i:nn
+ \else:
+ \exp_after:wN \use_ii:nn
+ \fi:
+ {#1}
+ { \if_false: { \fi: \@@_head_auxi:nw #1 } }
+ }
+\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: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}
+\cs_new:Npn \tl_tail:n #1
+ {
+ \__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}
+% \begin{macro}[pTF]{\tl_if_head_eq_charcode:nN}
+% \begin{macro}[pTF]{\tl_if_head_eq_charcode:fN}
+% \begin{macro}[pTF]{\tl_if_head_eq_catcode:nN}
+% 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 tokens: |?| which is taken in
+% the \cs{if_charcode:w} test, and \cs{use_none:nn}, which ensures
+% that \cs{prg_return_false:} is returned regardless of whether the
+% charcode test was \texttt{true} or \texttt{false}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 { p , T , F , TF }
+ {
+ \if_charcode:w
+ \exp_not:N #2
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ {
+ \exp_after:wN \exp_not:N
+ \tl_head:w #1 { ? \use_none:nn } \q_stop
+ }
+ { \str_head:n {#1} }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\prg_generate_conditional_variant:Nnn \tl_if_head_eq_charcode:nN
+ { 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 \cs{prg_return_true:} and \cs{else:} with \cs{use_none:nn}
+% in case the catcode test with the (arbitrarily chosen) |?| is
+% \texttt{true}.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1 #2 { p , T , F , TF }
+ {
+ \if_catcode:w
+ \exp_not:N #2
+ \tl_if_head_is_N_type:nTF { #1 ? }
+ {
+ \exp_after:wN \exp_not:N
+ \tl_head:w #1 { ? \use_none:nn } \q_stop
+ }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \c_group_begin_token }
+ { \c_space_token }
+ }
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+% \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 the usual \cs{prg_return_true:} and \cs{else:}.
+% 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
+ }
+\cs_new:Npn \@@_if_head_eq_meaning_normal:nN #1 #2
+ {
+ \exp_after:wN \if_meaning:w
+ \tl_head:w #1 { ?? \use_none:nnn } \q_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:n
+ \else:
+ \prg_return_false:
+ \exp_after:wN \use_none:n
+ \fi:
+ {
+ \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}
+% \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:w}
+% 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, the line involving \cs{@@_if_head_is_N_type:w}
+% produces~|^| (and otherwise nothing). In the third case
+% (begin-group token), the lines involving \cs{exp_after:wN} produce a
+% single closing brace. The category code test is thus true exactly
+% in the fourth case, which is what we want. One cannot optimize by
+% moving one of the |*| to the beginning: if |#1| contains primitive
+% conditionals, all of its occurrences must be dealt with before the
+% \cs{if_catcode: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_catcode:w
+ \if_false: { \fi: \@@_if_head_is_N_type:w ? #1 ~ }
+ \exp_after:wN \use_none:n
+ \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } }
+ * *
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_if_head_is_N_type:w #1 ~
+ {
+ \tl_if_empty:oTF { \use_none:n #1 } { ^ } { }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_head_is_group:n}
+% 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_catcode:w
+ \exp_after:wN \use_none:n
+ \exp_after:wN { \exp_after:wN { \token_to_str:N #1 ? } }
+ * *
+ \prg_return_false:
+ \else:
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \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 |?#1?~|. If that is a single~|?| 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
+% \cs{exp:w} and \cs{exp_end:} ensure 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 }
+ {
+ \exp:w \if_false: { \fi:
+ \@@_if_head_is_space:w ? #1 ? ~ }
+ }
+\cs_new:Npn \@@_if_head_is_space:w #1 ~
+ {
+ \tl_if_empty:oTF { \use_none:n #1 }
+ { \exp_after:wN \exp_end: \exp_after:wN \prg_return_true: }
+ { \exp_after:wN \exp_end: \exp_after:wN \prg_return_false: }
+ \exp_after:wN \use_none:n \exp_after:wN { \if_false: } \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+% \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{quark_if_recursion_tail_stop:n} 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
+ {
+ \quark_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 > 0 \exp_stop_f:
+ \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 < 0 \exp_stop_f:
+ \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:Nx #1 { \token_to_str:N #2 = \exp_not:o {#2} } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_show:n, \@@_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_new_protected:Npn \@@_show:n #1
+ {
+ \tl_set:Nf \l_@@_internal_a_tl { \@@_show:w #1 \q_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 . \q_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_log:n}
+% 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 }
+% \end{macrocode}
+% \end{macro}
+%
+% \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}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3token.dtx b/macros/latex/contrib/l3kernel/l3token.dtx
new file mode 100644
index 0000000000..9b9946a4f8
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3token.dtx
@@ -0,0 +1,2505 @@
+% \iffalse meta-comment
+%
+%% File: l3token.dtx
+%
+% Copyright (C) 2005-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package\\ Token manipulation^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \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{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
+% \meta{function}. 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}. 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 $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.
+% Active characters cannot be generated in older versions of \XeTeX{}.
+% \begin{texnote}
+% Exactly two expansions are needed to produce the character.
+% \end{texnote}
+% \end{function}
+%
+% \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{intexpr_1} \Arg{intexpr_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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Sets up the behaviour of the \meta{character} when
+% found inside \cs{tl_lower_case: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{intexpr_1} \Arg{intexpr_2}
+% \end{syntax}
+% Sets up the behaviour of the \meta{character} when
+% found inside \cs{tl_upper_case: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{intexpr_1} \Arg{intexpr_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{intexpr_1} \Arg{intexpr_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}
+% {
+% \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.
+% \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 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]{\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} renamed.
+% 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}
+%
+% \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_alignment: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, 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 = 2011-05-23, 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.
+% \end{function}
+%
+% \section{Peeking ahead at the next token}
+%
+% There is often a need to look ahead at the next token in the input
+% stream while leaving it in place. This is handled using the
+% \enquote{peek} functions. The generic \cs{peek_after:Nw} is
+% provided along with a family of predefined tests for common cases.
+% As peeking ahead does \emph{not} skip spaces the predefined tests
+% include both a space-respecting and space-skipping version.
+%
+% \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_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_catcode_ignore_spaces:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and
+% removed 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_catcode_remove_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_catcode_remove_ignore_spaces:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and
+% removed 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_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_charcode_ignore_spaces:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and removed 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 = 2012-12-20, TF]
+% {\peek_charcode_remove_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_charcode_remove_ignore_spaces:NTF} \meta{test token}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and
+% removed 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 = 2012-12-05, TF]{\peek_meaning_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_meaning_ignore_spaces:NTF} \meta{test token} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and
+% removed 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}[updated = 2012-12-05, TF]
+% {\peek_meaning_remove_ignore_spaces:N}
+% \begin{syntax}
+% \cs{peek_meaning_remove_ignore_spaces:NTF} \meta{test token}
+% ~~\Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next non-space \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}). Explicit and implicit space tokens
+% (with character code 32 and category code 10) are ignored and
+% removed 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}[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}
+%
+% \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).\footnote{In
+% \LuaTeX{}, there is also the case of \enquote{bytes}, which behave as
+% character tokens of category code $12$~(other) and character code
+% between $1114112$ and~$1114366$. They are used to output
+% individual bytes to files, rather than UTF-8.}
+% \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{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|.
+% \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 |$|),
+% \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 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{properties} |macro:|\meta{parameters}|->|\meta{replacement}
+% \end{quote}
+% where \meta{properties} is among \tn{protected}\tn{long}\tn{outer},
+% \meta{parameters} describes parameters that the macro expects, such as
+% |#1#2#3|, and \meta{replacement} describes how the parameters are
+% manipulated, such as~|#2/#1/#3|.
+%
+% ^^A todo Bruno: discuss here some other subtleties of space tokens? when looking for numbers, when looking for equal signs in let, in expressions, etc.
+%
+% 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}.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3token} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \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:cpx { #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 flagged up separately. That
+% done, hand off to the engine-dependent part.
+% \begin{macrocode}
+\cs_new:Npn \@@_generate_aux:w #1 ; #2 ;
+ {
+ \if_int_compare:w #2 = 10 \exp_stop_f:
+ \if_int_compare:w #1 = 0 \exp_stop_f:
+ \__kernel_msg_expandable_error:nn { kernel } { char-null-space }
+ \else:
+ \__kernel_msg_expandable_error:nn { kernel } { char-space }
+ \fi:
+ \else:
+ \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:
+ \__kernel_msg_expandable_error:nn { kernel }
+ { char-invalid-catcode }
+ \else:
+ \if_int_odd:w 0
+ \if_int_compare:w #1 < 0 \exp_stop_f: 1 \fi:
+ \if_int_compare:w #1 > \c_max_char_int 1 \fi: \exp_stop_f:
+ \__kernel_msg_expandable_error:nn { kernel }
+ { char-out-of-range }
+ \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. For
+% \LuaTeX{} and \XeTeX{} there is engine-level support.
+% They can do cases that macro emulation can't. All of those are filtered
+% out here using a primitive-based boolean expression to avoid fixing
+% the category code of the null character used in the false branch
+% (for 8-bit engines).
+% 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:
+%<*package>
+ \char_set_catcode_active:N \^^L
+ \cs_set:Npn ^^L { }
+%</package>
+ \char_set_catcode_other:n { 0 }
+ \if_int_odd:w 0
+ \sys_if_engine_luatex:T { 1 }
+ \sys_if_engine_xetex:T { 1 } \exp_stop_f:
+ \sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \exp_after:wN \exp_after:wN \exp_after:wN \exp_end:
+ \lua_now:e { l3kernel.charcat(#1, #2) }
+ }
+ }
+ {
+ \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:
+ }
+ \cs_if_exist:NF \tex_expanded:D
+ {
+ \cs_new_eq:NN \@@_generate_auxii:nnw \@@_generate_aux:nnw
+ \cs_gset:Npn \@@_generate_aux:nnw #1#2#3 \exp_end:
+ {
+ #3
+ \if_int_compare:w #2 = 13 \exp_stop_f:
+ \__kernel_msg_expandable_error:nn { kernel } { char-active }
+ \else:
+ \@@_generate_auxii:nnw {#1} {#2}
+ \fi:
+ \exp_end:
+ }
+ }
+ }
+ \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. There are a few things to notice here.
+% As |^^L| is |\outer| we need to locally set it to avoid a problem.
+% To get open/close braces into the list, they are set up using |\if_false:|
+% pairing and are then |x|-type expanded together into the desired form.
+% \begin{macrocode}
+ \tl_set:Nn \l_@@_tmp_tl { \exp_not:N \or: }
+ \char_set_catcode_group_begin:n { 0 } % {
+ \tl_put_right:Nn \l_@@_tmp_tl { ^^@ \if_false: } }
+ \char_set_catcode_group_end:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { { \fi: \exp_not:N \or: ^^@ } % }
+ \tl_set:Nx \l_@@_tmp_tl { \l_@@_tmp_tl }
+ \char_set_catcode_math_toggle:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \char_set_catcode_alignment:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: }
+ \char_set_catcode_parameter:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \char_set_catcode_math_superscript:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \char_set_catcode_math_subscript:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \tl_put_right:Nn \l_@@_tmp_tl { \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. We also
+% set up active tokens although they are (currently) filtered out by the
+% interface layer (\tn{Ucharcat} cannot make active tokens).
+% \begin{macrocode}
+ \char_set_catcode_space:n { 0 }
+ \tl_put_right:No \l_@@_tmp_tl { \use:n { \or: } ^^@ }
+ \char_set_catcode_letter:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \char_set_catcode_other:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+ \char_set_catcode_active:n { 0 }
+ \tl_put_right:Nn \l_@@_tmp_tl { \or: ^^@ }
+% \end{macrocode}
+% Convert the above temporary list into a series of constant token
+% lists, one for each character code, using \tn{tex_lowercase:D} to
+% convert |^^@| in each case. The \texttt{x}-type expansion ensures
+% that \tn{tex_lowercase:D} receives the contents of the token list.
+% In package mode, |^^L| is awkward hence this is done in three parts.
+% Notice that at this stage |^^@| is active.
+% \begin{macrocode}
+ \cs_set_protected:Npn \@@_tmp:n #1
+ {
+ \char_set_lccode:nn { 0 } {#1}
+ \char_set_lccode:nn { 32 } {#1}
+ \exp_args:Nx \tex_lowercase:D
+ {
+ \tl_const:Nn
+ \exp_not:c { c_@@_ \@@_int_to_roman:w #1 _tl }
+ { \exp_not:o \l_@@_tmp_tl }
+ }
+ }
+%<*package>
+ \int_step_function:nnN { 0 } { 11 } \@@_tmp:n
+ \group_begin:
+ \tl_replace_once:Nnn \l_@@_tmp_tl { ^^@ } { \ERROR }
+ \@@_tmp:n { 12 }
+ \group_end:
+ \int_step_function:nnN { 13 } { 255 } \@@_tmp:n
+%</package>
+%<*initex>
+ \int_step_function:nnN { 0 } { 255 } \@@_tmp:n
+%</initex>
+% \end{macrocode}
+% As \TeX{} is very unhappy if if 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:}.
+% \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 #2
+ \exp_last_unbraced:Nv \exp_stop_f:
+ { c_@@_ \@@_int_to_roman:w #1 _tl }
+ \or: }
+ \fi:
+ }
+ \fi:
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\c_catcode_other_space_tl}
+% Create a space with category code $12$: an \enquote{other} space.
+% \begin{macrocode}
+\tl_const:Nx \c_catcode_other_space_tl { \char_generate:nn { `\ } { 12 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Generic tokens}
+%
+% \begin{macrocode}
+%<@@=token>
+% \end{macrocode}
+%
+% \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}
+% {
+% \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_conditional:Npnn \token_if_eq_meaning:NN #1#2 { p , T , F , TF }
+ {
+ \if_meaning:w #1 #2
+ \prg_return_true: \else: \prg_return_false: \fi:
+ }
+% \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:x
+ {
+ \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 : }
+ \exp_not:N \q_stop
+ }
+ \cs_new:Npn \exp_not:N \@@_if_macro_p:w
+ ##1 \tl_to_str:n { ma } ##2 \c_colon_str ##3 \exp_not:N \q_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_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{q_stop}, and returns the first one and its delimiter.
+% This result is eventually compared to another string.
+% \begin{macrocode}
+\group_begin:
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \use:x
+ {
+ \cs_new:Npn \exp_not:c { @@_delimit_by_ #1 :w }
+ ####1 \tl_to_str:n {#1} ####2 \exp_not:N \q_stop
+ { ####1 \tl_to_str:n {#1} }
+ }
+ }
+\@@_tmp:w { char" }
+\@@_tmp:w { count }
+\@@_tmp:w { dimen }
+\@@_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_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{x}-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 five conditionals, \cs{cs_if_exist:cT} turns out to
+% be \texttt{false}, 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:x
+ {
+ \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} \exp_not:N \q_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 { 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}
+%^^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{q_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.
+%
+% \begin{macrocode}
+\tex_chardef:D \c_@@_A_int = `A ~ %
+\use:x
+ {
+ \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 { : : : } \exp_not:N \q_stop ##1
+ }
+ }
+ \cs_new:Npn \exp_not:N \@@_if_primitive:NNw
+ ##1##2 ##3 \c_colon_str ##4 \exp_not:N \q_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 \exp_not:N \q_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 \q_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}
+%
+% \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{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:Npx \@@_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}{\@@_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:Npx \@@_true_aux:w
+ {
+ \exp_not:N \group_align_safe_end:
+ \exp_not:n {#4}
+ }
+ \cs_set_eq:NN \@@_true:w #1
+ \cs_set:Npx \@@_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:cpx { 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_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
+% }
+% To ignore spaces, remove them using \cs{peek_remove_spaces:n}
+% before running the tests.
+% \begin{macrocode}
+\tl_map_inline:nn
+ {
+ { catcode } { catcode_remove }
+ { charcode } { charcode_remove }
+ { meaning } { meaning_remove }
+ }
+ {
+ \cs_new_protected:cpx { peek_#1_ignore_spaces:NTF } ##1##2##3
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_#1:NTF } ##1 {##2} {##3} }
+ }
+ \cs_new_protected:cpx { peek_#1_ignore_spaces:NT } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_#1:NT } ##1 {##2} }
+ }
+ \cs_new_protected:cpx { peek_#1_ignore_spaces:NF } ##1##2
+ {
+ \peek_remove_spaces:n
+ { \exp_not:c { peek_#1:NF } ##1 {##2} }
+ }
+ }
+% \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_q_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 \q_stop
+ {
+ \cs_new_protected:Npn \@@_execute_branches_N_type:
+ {
+ \if_int_odd:w
+ \if_catcode:w \exp_not:N \l_peek_token { 0 \exp_stop_f: \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } 0 \exp_stop_f: \fi:
+ \if_meaning:w \l_peek_token \c_space_token 0 \exp_stop_f: \fi:
+ 1 \exp_stop_f:
+ \exp_after:wN \@@_N_type:w
+ \token_to_meaning:N \l_peek_token
+ \q_mark \@@_N_type_aux:nnw
+ #1 \q_mark \use_none_delimit_by_q_stop:w
+ \q_stop
+ \exp_after:wN \@@_true:w
+ \else:
+ \exp_after:wN \@@_false:w
+ \fi:
+ }
+ \cs_new_protected:Npn \@@_N_type:w ##1 #1 ##2 \q_mark ##3
+ { ##3 {##1} {##2} }
+ }
+ \exp_after:wN \@@_tmp:w \tl_to_str:n { outer } \q_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}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/l3unicode.dtx b/macros/latex/contrib/l3kernel/l3unicode.dtx
new file mode 100644
index 0000000000..fbaa465935
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3unicode.dtx
@@ -0,0 +1,261 @@
+% \iffalse meta-comment
+%
+%% File: l3unicode.dtx
+%
+% Copyright (C) 2018-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "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} package: Unicode support functions^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides Unicode-specific functions along with loading data
+% from a range of Unicode Consortium files. At present, it provides no
+% public functions.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3unicode} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% Case changing both for strings and \enquote{text} requires data from
+% the Unicode Consortium. Some of this is build in to the format (as
+% \tn{lccode} and \tn{uccode} values) but this covers only the simple
+% one-to-one situations and does not fully handle for example case folding.
+%
+% 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. For performance reasons, some of the code here is very
+% low-level: the material is read during loading \pkg{expl3} in package
+% mode.
+% \begin{macrocode}
+\ior_new:N \g_@@_data_ior
+\bool_lazy_or:nnTF { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: }
+ {
+ \group_begin:
+% \end{macrocode}
+% Set up a private copy of the char-generation primitive.
+% \begin{macrocode}
+ \cs_set_eq:NN \@@_generate:w \tex_Uchar:D
+% \end{macrocode}
+% A fast local implementation for generating characters.
+% \begin{macrocode}
+ \cs_set:Npx \@@_generate:n #1
+ {
+ \sys_if_engine_luatex:TF
+ {
+ \exp_not:N \lua_now:e
+ {
+ l3kernel.charcat
+ (
+ \exp_not:N \tex_number:D #1 ,
+ \exp_not:N \tex_the:D \tex_catcode:D #1
+ )
+ }
+ }
+ {
+ \exp_not:N \tex_Ucharcat:D
+ #1 ~
+ \tex_catcode:D #1 ~
+ }
+ }
+% \end{macrocode}
+% Parse the main Unicode data file for title case exceptions (the one-to-one
+% lower and upper case mappings it contains are all be covered by the \TeX{}
+% data). There are no comments in the main data file so this can be done using
+% a standard mapping and no checks.
+% \begin{macrocode}
+ \ior_open:Nn \g_@@_data_ior { UnicodeData.txt }
+ \cs_set_protected:Npn \@@_data_auxi:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+ { \@@_data_auxii:w #1 ; }
+ \cs_set_protected:Npn \@@_data_auxii:w
+ #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 \q_stop
+ {
+ \cs_set_nopar:Npn \l_@@_tmpa_tl {#7}
+ \reverse_if:N \if_meaning:w \l_@@_tmpa_tl \c_empty_tl
+ \cs_set_nopar:Npn \l_@@_tmpb_tl {#5}
+ \reverse_if:N \if_meaning:w \l_@@_tmpa_tl \l_@@_tmpb_tl
+ \tl_const:cx
+ { c_@@_mixed_case_ \@@_generate:w "#1 _tl }
+ { \@@_generate:n { "#7 } }
+ \fi:
+ \fi:
+ }
+ \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
+ }
+ \ior_close:N \g_@@_data_ior
+% \end{macrocode}
+% 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}
+ \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
+ \char_value_lccode:n {"#1} = "#3 ~
+ \tl_const:cx
+ { c_@@_fold_case_ \@@_generate:w "#1 _tl }
+ { \@@_generate:n { "#3 } }
+ \fi:
+ \else:
+ \if:w \tl_head:n { #2 ? } F
+ \@@_data_auxii:w #1 ~ #3 ~ \q_stop
+ \fi:
+ \fi:
+ }
+ \cs_set_protected:Npn \@@_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
+ {
+ \tl_const:cx { c_@@_fold_case_ \@@_generate:w "#1 _tl }
+ {
+ \@@_generate:n { "#2 }
+ \@@_generate:n { "#3 }
+ \tl_if_blank:nF {#4}
+ { \@@_generate:n { \int_value:w "#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 lower casing special situations, there is a bit more to
+% do as we also have title casing 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 ~ mixed ~ #3 ~ } ~ \q_stop }
+ }
+ \cs_set_protected:Npn \@@_data_auxii:w
+ #1 ~ #2 ~ #3 ~ #4 ~ #5 \q_stop
+ {
+ \tl_if_empty:nF {#4}
+ {
+ \tl_const:cx { c_@@_ #2 _case_ \@@_generate:w "#1 _tl }
+ {
+ \@@_generate:n { "#3 }
+ \@@_generate:n { "#4 }
+ \tl_if_blank:nF {#5}
+ { \@@_generate:n { "#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}
+% For the $8$-bit engines, the above is skipped but there is still some
+% set up required. As case changing can only be applied to bytes, and
+% they have to be in the ASCII range, we define a series of data stores
+% to represent them, and the data are used such that only these are
+% ever case-changed. We do open and close one file to force allocation of
+% a read: this keeps all engines in line.
+% \begin{macrocode}
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:NN #1#2
+ {
+ \quark_if_recursion_tail_stop:N #2
+ \tl_const:cn { c_@@_upper_case_ #2 _tl } {#1}
+ \tl_const:cn { c_@@_lower_case_ #1 _tl } {#2}
+ \tl_const:cn { c_@@_fold_case_ #1 _tl } {#2}
+ \@@_tmp:NN
+ }
+ \@@_tmp:NN
+ AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
+ ? \q_recursion_tail \q_recursion_stop
+ \ior_open:Nn \g_@@_data_ior { UnicodeData.txt }
+ \ior_close:N \g_@@_data_ior
+ \group_end:
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex
diff --git a/macros/latex/contrib/l3kernel/source3.pdf b/macros/latex/contrib/l3kernel/source3.pdf
new file mode 100644
index 0000000000..35d62f49f4
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/source3.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/source3.tex b/macros/latex/contrib/l3kernel/source3.tex
new file mode 100644
index 0000000000..021b8111cf
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/source3.tex
@@ -0,0 +1,98 @@
+\iffalse meta-comment
+
+File: source3.tex
+
+Copyright (C) 1990-2012,2017-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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 780 pages).
+%
+% 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}
+\listfiles
+
+\begin{document}
+
+\title{The \LaTeX3 Sources}
+\author{%
+ The \LaTeX3 Project\thanks
+ {%
+ E-mail:
+ \href{mailto:latex-team@latex-project.org}
+ {latex-team@latex-project.org}%
+ }%
+}
+\date{Released 2019-08-25}
+
+\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{}
+\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/macros/latex/contrib/l3kernel/source3body.tex b/macros/latex/contrib/l3kernel/source3body.tex
new file mode 100644
index 0000000000..620ac44f84
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/source3body.tex
@@ -0,0 +1,494 @@
+\iffalse meta-comment
+
+File: source3body.tex
+
+Copyright (C) 1990-2012,2014-2019 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ https://www.latex-project.org/lppl.txt
+
+This file is part of the "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
+This is the reference documentation for the \pkg{expl3}
+programming environment. The \pkg{expl3} modules set up an experimental
+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.
+
+At present, the \pkg{expl3} modules are designed to be loaded on top of
+\LaTeXe{}. In time, a \LaTeX3 format will be produced based on this code.
+This allows the code to be used in \LaTeXe{} packages \emph{now} while a
+stand-alone \LaTeX3 is developed.
+
+\begin{bfseries}
+ While \pkg{expl3} is still experimental, the bundle is now regarded as
+ broadly stable. 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}.
+
+ New modules will be added to the distributed version of \pkg{expl3} as
+ they reach maturity.
+\end{bfseries}
+
+\end{abstract}
+
+\clearpage
+
+{%
+ \def\\{:}% fix "newlines" in the ToC
+ \tableofcontents
+}
+
+\clearpage
+\pagenumbering{arabic}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% 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
+\def\partname{Part}
+\def\maketitle{\clearpage\part{\@title}}
+\let\thanks\@gobble
+\let\DelayPrintIndex\PrintIndex
+\let\PrintIndex\@empty
+\providecommand*{\hexnum}[1]{\text{\texttt{\char`\"}#1}}
+\makeatother
+
+\part{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|.
+ \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 with a very different implementation.
+ Functions which feature an \texttt{e}-type argument may be
+ expandable. The drawback is that \texttt{e} is extremely slow
+ (often more than $200$ times slower) in older engines, more
+ precisely in non-\LuaTeX{} engines older than 2019.
+ \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} specifier means \emph{do not use}.
+ All of the \TeX{} primitives are initially \cs{let} to a \texttt{D}
+ name, and some are then given a second name. Only the kernel
+ team should use anything with a \texttt{D} specifier!
+\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{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{seq}] \enquote{Sequence}: a data-type used to implement
+ lists (with access at both ends) and stacks.
+ \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}] 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.
+\end{description}
+
+\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{\cs{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 \cs{outer} tokens are used in the arguments.
+
+\DisableImplementation
+
+\DocInput{l3bootstrap.dtx}
+\DocInput{l3names.dtx}
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist { l3kernel-functions.dtx }
+\ExplSyntaxOff
+\DocInput{l3basics.dtx}
+\DocInput{l3expan.dtx}
+\DocInput{l3tl.dtx}
+\DocInput{l3str.dtx}
+\DocInput{l3str-convert.dtx}
+\DocInput{l3quark.dtx}
+\DocInput{l3seq.dtx}
+\DocInput{l3int.dtx}
+\DocInput{l3flag.dtx}
+\DocInput{l3prg.dtx}
+\DocInput{l3sys.dtx}
+\DocInput{l3clist.dtx}
+\DocInput{l3token.dtx}
+\DocInput{l3prop.dtx}
+\DocInput{l3msg.dtx}
+\DocInput{l3file.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 ,
+ }
+\ExplSyntaxOff
+
+\DocInput{l3fparray.dtx}
+\DocInput{l3sort.dtx}
+\DocInput{l3tl-analysis.dtx}
+\DocInput{l3regex.dtx}
+\DocInput{l3box.dtx}
+\DocInput{l3coffins.dtx}
+\DocInput{l3color-base.dtx}
+\DocInput{l3luatex.dtx}
+\DocInput{l3unicode.dtx}
+\DocInput{l3legacy.dtx}
+\DocInput{l3candidates.dtx}
+
+\ExplSyntaxOn
+\clist_gput_right:Nn \g_docinput_clist { l3deprecation.dtx }
+\ExplSyntaxOff
+
+\endinput