diff options
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3fp-convert.dtx')
-rw-r--r-- | macros/latex/contrib/l3kernel/l3fp-convert.dtx | 621 |
1 files changed, 621 insertions, 0 deletions
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 |