summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3str.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3str.dtx458
1 files changed, 74 insertions, 384 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
index 245be5a85ce..4cf93f4d10b 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
@@ -29,7 +29,7 @@
% \fi
%
% \title{^^A
-% The \pkg{l3str} package\\Strings^^A
+% The \pkg{l3str} package: Strings^^A
% }
%
% \author{^^A
@@ -41,7 +41,7 @@
% }^^A
% }
%
-% \date{Released 2018/03/05}
+% \date{Released 2018-04-30}
%
% \maketitle
%
@@ -203,7 +203,7 @@
% var} are not automatically converted to a string.
% \end{function}
%
-% \subsection{Modifying string variables}
+% \section{Modifying string variables}
%
% \begin{function}[added = 2017-10-08]
% {
@@ -269,7 +269,7 @@
% results in \cs{l_tmpa_str} containing \texttt{abcd}.
% \end{function}
%
-% \subsection{String conditionals}
+% \section{String conditionals}
%
% \begin{function}[EXP, pTF, added = 2015-09-18]
% {\str_if_exist:N, \str_if_exist:c}
@@ -799,70 +799,6 @@
% code and so should only be used for short-term storage.
% \end{variable}
%
-% \subsection{Internal string functions}
-%
-% \begin{function}[EXP]{\__str_if_eq_x:nn}
-% \begin{syntax}
-% \cs{__str_if_eq_x:nn} \Arg{tl_1} \Arg{tl_2}
-% \end{syntax}
-% Compares the full expansion of two \meta{token lists} on a character by
-% character basis, and is \texttt{true} if the two lists contain the same
-% characters in the same order. Leaves |0| in the input stream if the
-% condition is true, and |+1| or |-1| otherwise.
-% \end{function}
-%
-% \begin{function}{\__str_if_eq_x_return:nn}
-% \begin{syntax}
-% \cs{__str_if_eq_x_return:nn} \Arg{tl_1} \Arg{tl_2}
-% \end{syntax}
-% Compares the full expansion of two \meta{token lists} on a character by
-% character basis, and is \texttt{true} if the two lists contain the same
-% characters in the same order. Either \cs{prg_return_true:} or
-% \cs{prg_return_false:} is then left in the input stream. This is a version
-% of \cs{str_if_eq_x:nnTF} coded for speed.
-% \end{function}
-%
-% \begin{function}[EXP]{\__str_to_other:n}
-% \begin{syntax}
-% \cs{__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]{\__str_to_other_fast:n}
-% \begin{syntax}
-% \cs{__str_to_other_fast:n} \Arg{token list}
-% \end{syntax}
-% Same behaviour \cs{__str_to_other:n} but only restricted-expandable.
-% It takes a time linear in the character count of the string.
-% It is used for \cs{iow_wrap:nnnN}.
-% \end{function}
-%
-% \begin{function}[EXP]{\__str_count:n}
-% \begin{syntax}
-% \cs{__str_count:n} \Arg{other string}
-% \end{syntax}
-% This function expects an argument that is entirely made of
-% characters with category \enquote{other}, as produced by
-% \cs{__str_to_other:n}. It leaves in the input stream the number of
-% character tokens in the \meta{other string}, faster than the
-% analogous \cs{str_count:n} function.
-% \end{function}
-%
-% \begin{function}[EXP]{\__str_range:nnn}
-% \begin{syntax}
-% \cs{__str_range:nnn} \Arg{other string} \Arg{start index} \Arg{end index}
-% \end{syntax}
-% Identical to \cs{str_range:nnn} except that the first argument is
-% expected to be entirely made of characters with category
-% \enquote{other}, as produced by \cs{__str_to_other:n}, and the
-% result is also an \meta{other string}.
-% \end{function}
-%
% \end{documentation}
%
% \begin{implementation}
@@ -955,8 +891,11 @@
\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} } }
- \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :Nn } { Nx , cn , cx }
+ {
+ \exp_not:c { tl_ #1 :Nx } ##1
+ { \exp_not:N \tl_to_str:n {##2} }
+ }
+ \cs_generate_variant:cn { str_ #1 :Nn } { Nx , cn , cx }
\@@_tmp:n
}
}
@@ -1079,10 +1018,14 @@
% }
% More copy-paste!
% \begin{macrocode}
-\prg_new_eq_conditional:NNn \str_if_exist:N \tl_if_exist:N { p , T , F , TF }
-\prg_new_eq_conditional:NNn \str_if_exist:c \tl_if_exist:c { p , T , F , TF }
-\prg_new_eq_conditional:NNn \str_if_empty:N \tl_if_empty:N { p , T , F , TF }
-\prg_new_eq_conditional:NNn \str_if_empty:c \tl_if_empty:c { p , T , F , TF }
+\prg_new_eq_conditional:NNn \str_if_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}
%
@@ -1093,9 +1036,8 @@
% 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 covered in \pkg{l3boostrap}: long-term this may
-% need to go into a separate Lua file, but at present it's somewhere that
-% spaces are not skipped for ease-of-input. The need to detokenize and force
+% 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_x:nn {#} { \tl_to_str:n {#} }|, which
% otherwise would fail as \cs{luatex_luaescapestring:D} does not double
@@ -1104,9 +1046,11 @@
\cs_new:Npn \@@_if_eq_x:nn #1#2 { \pdftex_strcmp:D {#1} {#2} }
\cs_if_exist:NT \luatex_luatexversion:D
{
+ \cs_set_eq:NN \lua_escape_x:n \luatex_luaescapestring:D
+ \cs_set_eq:NN \lua_now_x:n \luatex_directlua:D
\cs_set:Npn \@@_if_eq_x:nn #1#2
{
- \luatex_directlua:D
+ \lua_now_x:n
{
l3kernel.strcmp
(
@@ -1117,34 +1061,14 @@
}
\cs_new:Npn \@@_escape_x:n #1
{
- \luatex_luaescapestring:D
- {
- \etex_detokenize:D \exp_after:wN { \luatex_expanded:D {#1} }
- }
+ \lua_escape_x:n
+ { \__kernel_tl_to_str:w \use_x:n { {#1} } }
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
-% \begin{macro}[EXP]{\@@_if_eq_x_return:nn}
-% It turns out that we often need to compare a token list
-% with the result of applying some function to it, and
-% return with \cs{prg_return_true/false:}. This test is
-% similar to \cs{str_if_eq:nnTF} (see \pkg{l3str}),
-% but is hard-coded for speed.
-% \begin{macrocode}
-\cs_new:Npn \@@_if_eq_x_return:nn #1 #2
- {
- \if_int_compare:w \@@_if_eq_x:nn {#1} {#2} = 0 \exp_stop_f:
- \prg_return_true:
- \else:
- \prg_return_false:
- \fi:
- }
-% \end{macrocode}
-% \end{macro}
-%
% \begin{macro}[pTF, EXP]
% {
% \str_if_eq:nn, \str_if_eq:Vn, \str_if_eq:on, \str_if_eq:nV,
@@ -1180,7 +1104,8 @@
% \begin{macrocode}
\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F }
{
- \if_int_compare:w \@@_if_eq_x:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 }
+ \if_int_compare:w
+ \@@_if_eq_x: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
@@ -1278,7 +1203,8 @@
{ \@@_case_end:nw {#3} }
{ \@@_case_x:nw {#1} }
}
-\cs_new_eq:NN \@@_case_end:nw \__prg_case_end:nw
+\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}
@@ -1299,7 +1225,7 @@
% 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{@@_to_other:n}, quadratic in the string length. To deal
+% 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
@@ -1317,9 +1243,9 @@
{
\exp_after:wN \@@_map_function:w
\exp_after:wN \@@_map_function:Nn \exp_after:wN #2
- \etex_detokenize:D {#1}
+ \__kernel_tl_to_str:w {#1}
\q_recursion_tail ? ~
- \__prg_break_point:Nn \str_map_break: { }
+ \prg_break_point:Nn \str_map_break: { }
}
\cs_new:Npn \str_map_function:NN
{ \exp_args:No \str_map_function:nN }
@@ -1335,24 +1261,25 @@
\cs_generate_variant:Nn \str_map_function:NN { c }
\cs_new_protected:Npn \str_map_inline:nn #1#2
{
- \int_gincr:N \g__prg_map_int
+ \int_gincr:N \g__kernel_prg_map_int
\cs_gset_protected:cpn
- { __prg_map_ \int_use:N \g__prg_map_int :w } ##1 {#2}
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
\use:x
{
\exp_not:N \@@_map_inline:NN
- \exp_not:c { __prg_map_ \int_use:N \g__prg_map_int :w }
- \@@_to_other_fast:n {#1}
+ \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__prg_map_int }
+ \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:
+ \quark_if_recursion_tail_break:NN #2 \str_map_break:
\exp_args:No #1 { \token_to_str:N #2 }
\@@_map_inline:NN #1
}
@@ -1361,25 +1288,25 @@
\use:x
{
\exp_not:n { \@@_map_variable:NnN #2 {#3} }
- \@@_to_other_fast:n {#1}
+ \__kernel_str_to_other_fast:n {#1}
}
\q_recursion_tail
- \__prg_break_point:Nn \str_map_break: { }
+ \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:
+ \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: { } }
+ { \prg_map_break:Nn \str_map_break: { } }
\cs_new:Npn \str_map_break:n
- { \__prg_map_break:Nn \str_map_break: }
+ { \prg_map_break:Nn \str_map_break: }
% \end{macrocode}
% \end{macro}
% \end{macro}
@@ -1394,7 +1321,7 @@
%
% \subsection{Accessing specific characters in a string}
%
-% \begin{macro}[EXP]{\@@_to_other:n}
+% \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
@@ -1405,7 +1332,7 @@
% 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 \@@_to_other:n #1
+\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
@@ -1432,13 +1359,13 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[rEXP]{\@@_to_other_fast:n}
-% \begin{macro}[rEXP]{\@@_to_other_fast_loop:w, \@@_to_other_fast_end:w}
-% The difference with \cs{@@_to_other:n} is that the converted part is
+% \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 \@@_to_other_fast:n #1
+\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
@@ -1491,17 +1418,16 @@
\exp_args:Nf \tl_to_str:n
{
\exp_args:Nf \@@_item:nn
- { \@@_to_other:n {#1} } {#2}
+ { \__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} } }
-\__kernel_patch_args:nNNpn { {#1} { \__int_eval:n {#2} } }
\cs_new:Npn \@@_item:nn #1#2
{
\exp_after:wN \@@_item:w
- \__int_value:w \__int_eval:w #2 \exp_after:wN ;
- \__int_value:w \@@_count:n {#1} ;
+ \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;
@@ -1513,7 +1439,7 @@
{
\exp_after:wN \use_i_delimit_by_q_stop:nw
\exp:w \exp_after:wN \@@_skip_exp_end:w
- \__int_value:w \__int_eval:w #1 + #2 ;
+ \int_value:w \int_eval:n { #1 + #2 } ;
}
}
{
@@ -1552,12 +1478,15 @@
\exp_after:wN \@@_skip_loop:wNNNNNNNN
\else:
\exp_after:wN \@@_skip_end:w
- \__int_value:w \__int_eval: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:w #1 - 8 ; }
+ {
+ \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
@@ -1588,19 +1517,17 @@
\exp_args:Nf \tl_to_str:n
{
\exp_args:Nf \@@_range:nnn
- { \@@_to_other:n {#1} } {#2} {#3}
+ { \__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} } }
-\__kernel_patch_args:nNNpn
- { {#1} { \__int_eval:n {#2} } { \__int_eval:n {#3} } }
\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:w #2 - 1 \exp_after:wN ;
- \__int_value:w \__int_eval:w #3 ;
+ \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;
@@ -1612,7 +1539,7 @@
\cs_new:Npn \@@_range:nnw #1#2
{
\exp_after:wN \@@_collect_delimit_by_q_stop:w
- \__int_value:w \__int_eval:w #2 - #1 \exp_after:wN ;
+ \int_value:w \int_eval:n { #2 - #1 } \exp_after:wN ;
\exp:w \@@_skip_exp_end:w #1 ;
}
% \end{macrocode}
@@ -1678,14 +1605,15 @@
\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:w #1 - 7 ;
+ \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:
+ \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 }
@@ -1733,7 +1661,7 @@
% \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{@@_to_other:n}, then pass the result to \cs{tl_count:n}.
+% 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
@@ -1881,8 +1809,6 @@
% \begin{macro}[EXP]{\@@_change_case_loop:nw}
% \begin{macro}[EXP]{\@@_change_case_space:n}
% \begin{macro}[EXP]{\@@_change_case_char:nN}
-% \begin{macro}
-% {\@@_lookup_lower:N, \@@_lookup_upper:N, \@@_lookup_fold:N}
% 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
@@ -1907,7 +1833,8 @@
\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 { #2 }
+\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}
@@ -1915,8 +1842,8 @@
{ \@@_change_case_char:nN }
{#1} #2 \q_recursion_stop
}
-\use:x
- { \cs_new:Npn \exp_not:N \@@_change_case_space:n ##1 \c_space_tl }
+\exp_last_unbraced:NNNNo
+ \cs_new:Npn \@@_change_case_space:n #1 \c_space_tl
{
\@@_change_case_output:nw { ~ }
\@@_change_case_loop:nw {#1}
@@ -1925,41 +1852,11 @@
{
\quark_if_recursion_tail_stop_do:Nn #2
{ \@@_change_case_end:wn }
- \cs_if_exist:cTF { c__unicode_ #1 _ #2 _tl }
- {
- \@@_change_case_output:fw
- { \tl_to_str:c { c__unicode_ #1 _ #2 _tl } }
- }
- { \@@_change_case_char_aux:nN {#1} #2 }
+ \@@_change_case_output:fw
+ { \use:c { char_ #1 _case:N } #2 }
\@@_change_case_loop:nw {#1}
}
% \end{macrocode}
-% For Unicode engines there's a look up to see if the current character
-% has a valid one-to-one case change mapping. That's not needed for $8$-bit
-% engines: as they don't have \cs{utex_char:D} all of the changes they can
-% make are hard-coded and so already picked up above.
-% \begin{macrocode}
-\cs_if_exist:NTF \utex_char:D
- {
- \cs_new:Npn \@@_change_case_char_aux:nN #1#2
- {
- \int_compare:nNnTF { \use:c { __str_lookup_ #1 :N } #2 } = { 0 }
- { \@@_change_case_output:nw {#2} }
- {
- \@@_change_case_output:fw
- { \utex_char:D \use:c { __str_lookup_ #1 :N } #2 ~ }
- }
- }
- \cs_new_protected:Npn \@@_lookup_lower:N #1 { \tex_lccode:D `#1 }
- \cs_new_protected:Npn \@@_lookup_upper:N #1 { \tex_uccode:D `#1 }
- \cs_new_eq:NN \@@_lookup_fold:N \@@_lookup_lower:N
- }
- {
- \cs_new:Npn \@@_change_case_char_aux:nN #1#2
- { \@@_change_case_output:nw {#2} }
- }
-% \end{macrocode}
-% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
@@ -2024,213 +1921,6 @@
% \end{macrocode}
% \end{macro}
%
-% \subsection{Unicode data for case changing}
-%
-% \begin{macrocode}
-%<@@=unicode>
-% \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.
-%
-% The data required for cross-module manipulations is loaded here: currently
-% this means for |str| and |tl| functions. As such, the prefix used is not
-% |str| but rather |unicode|. For performance (as the entire data set must
-% be read during each run) and as this code comes somewhat early in the
-% load process, there is quite a bit of low-level code here.
-%
-% As only the data needs to remain at the end of this process, everything
-% is set up inside a group.
-% \begin{macrocode}
-\group_begin:
-% \end{macrocode}
-% A read stream is needed. The I/O module is not yet in place \emph{and}
-% we do not want to use up a stream. We therefore use a known free one in
-% format mode or look for the next free one in package mode (covers plain,
-% \LaTeXe{} and Con\TeX{}t MkII and MkIV).
-% \begin{macrocode}
-%<*initex>
- \tex_chardef:D \g_@@_data_ior = 0 \scan_stop:
-%</initex>
-%<*package>
- \tex_chardef:D \g_@@_data_ior
- \etex_numexpr:D
- \cs_if_exist:NTF \lastallocatedread
- { \lastallocatedread }
- {
- \cs_if_exist:NTF \c_syst_last_allocated_read
- { \c_syst_last_allocated_read }
- { \tex_count:D 16 ~ }
- }
- + 1
- \scan_stop:
-%</package>
-% \end{macrocode}
-% Set up to read each file. As they use C-style comments, there is a need to
-% deal with |#|. At the same time, spaces are important so they need to be
-% picked up as they are important. Beyond that, the current category code
-% scheme works fine. With no I/O loop available, hard-code one that works
-% quickly.
-% \begin{macrocode}
- \cs_set_protected:Npn \@@_map_inline:n #1
- {
- \group_begin:
- \tex_catcode:D `\# = 12 \scan_stop:
- \tex_catcode:D `\ = 10 \scan_stop:
- \tex_openin:D \g_@@_data_ior = #1 \scan_stop:
- \cs_if_exist:NT \utex_char:D
- { \@@_map_loop: }
- \tex_closein:D \g_@@_data_ior
- \group_end:
- }
- \cs_set_protected:Npn \@@_map_loop:
- {
- \tex_ifeof:D \g_@@_data_ior
- \exp_after:wN \use_none:n
- \else:
- \exp_after:wN \use:n
- \fi:
- {
- \tex_read:D \g_@@_data_ior to \l_@@_tmp_tl
- \if_meaning:w \c_empty_tl \l_@@_tmp_tl
- \else:
- \exp_after:wN \@@_parse:w \l_@@_tmp_tl \q_stop
- \fi:
- \@@_map_loop:
- }
- }
-% \end{macrocode}
-% The lead-off parser for each line is common for all of the files. If
-% the line starts with a |#| it's a comment. There's one special comment
-% line to look out for in \texttt{SpecialCasing.txt} as we want to ignore
-% everything after it. As this line does not appear in any other sources
-% and the test is quite quick (there are relatively few comment lines), it
-% can be present in all of the passes.
-% \begin{macrocode}
- \cs_set_protected:Npn \@@_parse:w #1#2 \q_stop
- {
- \reverse_if:N \if:w \c_hash_str #1
- \@@_parse_auxi:w #1#2 \q_stop
- \else:
- \if_int_compare:w \__str_if_eq_x:nn
- { \exp_not:n {#2} } { ~Conditional~Mappings~ } = 0 \exp_stop_f:
- \cs_set_protected:Npn \@@_parse:w ##1 \q_stop { }
- \fi:
- \fi:
- }
-% \end{macrocode}
-% Storing each exception is always done in the same way: create a constant
-% token list which expands to exactly the mapping. These have the
-% category codes \enquote{now} (so should be letters) but are later detokenized
-% for string use.
-% \begin{macrocode}
- \cs_set_protected:Npn \@@_store:nnnnn #1#2#3#4#5
- {
- \tl_const:cx { c_@@_ #2 _ \utex_char:D "#1 _tl }
- {
- \utex_char:D "#3 ~
- \utex_char:D "#4 ~
- \tl_if_blank:nF {#5}
- { \utex_char:D "#5 }
- }
- }
-% \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).
-% \begin{macrocode}
- \cs_set_protected:Npn \@@_parse_auxi:w
- #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
- { \@@_parse_auxii:w #1 ; }
- \cs_set_protected:Npn \@@_parse_auxii:w
- #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 \q_stop
- {
- \tl_if_blank:nF {#7}
- {
- \if_int_compare:w \__str_if_eq_x:nn { #5 ~ } {#7} = 0 \exp_stop_f:
- \else:
- \tl_const:cx
- { c_@@_mixed_ \utex_char:D "#1 _tl }
- { \utex_char:D "#7 }
- \fi:
- }
- }
- \@@_map_inline:n { UnicodeData.txt }
-% \end{macrocode}
-% 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}
- \cs_set_protected:Npn \@@_parse_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
- {
- \if_int_compare:w \__str_if_eq_x:nn {#2} { C } = 0 \exp_stop_f:
- \if_int_compare:w \tex_lccode:D "#1 = "#3 \scan_stop:
- \else:
- \tl_const:cx
- { c_@@_fold_ \utex_char:D "#1 _tl }
- { \utex_char:D "#3 ~ }
- \fi:
- \else:
- \if_int_compare:w \__str_if_eq_x:nn {#2} { F } = 0 \exp_stop_f:
- \@@_parse_auxii:w #1 ~ #3 ~ \q_stop
- \fi:
- \fi:
- }
- \cs_set_protected:Npn \@@_parse_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
- { \@@_store:nnnnn {#1} { fold } {#2} {#3} {#4} }
- \@@_map_inline:n { CaseFolding.txt }
-% \end{macrocode}
-% For upper and lower casing special situations, there is a bit more to
-% do as we also have title casing to consider.
-% \begin{macrocode}
- \cs_set_protected:Npn \@@_parse_auxi:w #1 ;~ #2 ;~ #3 ;~ #4 ; #5 \q_stop
- {
- \use:n { \@@_parse_auxii:w #1 ~ lower ~ #2 ~ } ~ \q_stop
- \use:n { \@@_parse_auxii:w #1 ~ upper ~ #4 ~ } ~ \q_stop
- \if_int_compare:w \__str_if_eq_x:nn {#3} {#4} = 0 \exp_stop_f:
- \else:
- \use:n { \@@_parse_auxii:w #1 ~ mixed ~ #3 ~ } ~ \q_stop
- \fi:
- }
- \cs_set_protected:Npn \@@_parse_auxii:w #1 ~ #2 ~ #3 ~ #4 ~ #5 \q_stop
- {
- \tl_if_empty:nF {#4}
- { \@@_store:nnnnn {#1} {#2} {#3} {#4} {#5} }
- }
- \@@_map_inline:n { SpecialCasing.txt }
-% \end{macrocode}
-% For the $8$-bit engines, the above does nothing but there is some set
-% up needed. There is no expandable character generator primitive so some
-% alternative is needed. As we've not used up hash space for the above, we
-% can go for the fast approach here of one name per letter. Keeping folding
-% and lower casing separate makes the use later a bit easier.
-% \begin{macrocode}
- \cs_if_exist:NF \utex_char:D
- {
- \cs_set_protected:Npn \@@_tmp:NN #1#2
- {
- \if_meaning:w \q_recursion_tail #2
- \exp_after:wN \use_none_delimit_by_q_recursion_stop:w
- \fi:
- \tl_const:cn { c_@@_fold_ #1 _tl } {#2}
- \tl_const:cn { c_@@_lower_ #1 _tl } {#2}
- \tl_const:cn { c_@@_upper_ #2 _tl } {#1}
- \@@_tmp:NN
- }
- \@@_tmp:NN
- AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
- ? \q_recursion_tail \q_recursion_stop
- }
-% \end{macrocode}
-%
-% All done: tidy up.
-% \begin{macrocode}
-\group_end:
-% \end{macrocode}
-%
% \begin{macrocode}
%</initex|package>
% \end{macrocode}