diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx | 667 |
1 files changed, 438 insertions, 229 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx index 03bd96b4b81..541d8f14ab6 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-parse.dtx Copyright (C) 2011-2017 The LaTeX3 Project +%% File: l3fp-parse.dtx Copyright (C) 2011-2018 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 @@ -28,7 +28,7 @@ %</driver> % \fi % -% \title{The \textsf{l3fp-parse} package +% \title{The \textsf{l3fp-parse} package\\ % Floating point expression parsing} % \author{^^A % The \LaTeX3 Project\thanks @@ -38,7 +38,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2017/12/16} +% \date{Released 2018/02/21} % % \maketitle % @@ -65,12 +65,17 @@ % (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 an internal floating point number. This +% 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. % @@ -83,33 +88,8 @@ % \end{texnote} % \end{macro} % -% 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 with multiple arguments. -% \item[15] Function calls expecting exactly one argument. -% \item[13/14] Binary |**| and~|^| (right to left). -% \item[12] Unary |+|, |-|, |!| (right to left). -% \item[10] Binary |*|, |/|, and juxtaposition (implicit~|*|). -% \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, and parentheses accepting commas. -% \item[1] Parentheses expecting exactly one argument. -% \item[0] Start and end of the expression. -% \end{itemize} -% % \begin{variable} % { -% \c_@@_prec_funcii_int, % \c_@@_prec_func_int, % \c_@@_prec_hatii_int, % \c_@@_prec_hat_int, @@ -122,12 +102,33 @@ % \c_@@_prec_quest_int, % \c_@@_prec_colon_int, % \c_@@_prec_comma_int, -% \c_@@_prec_paren_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[10] Binary |*|, |/|, and juxtaposition (implicit~|*|). +% \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_funcii_int { 16 } -\int_const:Nn \c_@@_prec_func_int { 15 } +\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 } @@ -139,7 +140,7 @@ \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_paren_int { 1 } +\int_const:Nn \c_@@_prec_tuple_int { 1 } \int_const:Nn \c_@@_prec_end_int { 0 } % \end{macrocode} % \end{variable} @@ -168,7 +169,7 @@ % 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 much better to avoid requiring the closing +% 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. @@ -232,10 +233,11 @@ % 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|. 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. +% 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. @@ -247,16 +249,16 @@ % \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 +% \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 +% \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 +% \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 @@ -289,10 +291,10 @@ % 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{syntax} -% \meta{number} +% \begin{quote} +% \meta{number}\\ % | \__fp_parse_infix_|\meta{operator}|:N| \meta{precedence} -% \end{syntax} +% \end{quote} % expanding the \texttt{infix} auxiliary before leaving the above in the % input stream. % @@ -638,29 +640,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[EXP]{\@@_type_from_scan:N, \@@_type_from_scan:w} -% \begin{syntax} -% \cs{@@_type_from_scan:N} \meta{token} -% \end{syntax} -% 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:Npx \@@_type_from_scan: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 - } -\use:x - { - \cs_new:Npn \exp_not:N \@@_type_from_scan:w - ##1 \tl_to_str:n { s_@@ } ##2 \exp_not:N \q_mark ##3 \exp_not:N \q_stop - {##2} - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}[rEXP] % { % \@@_parse_digits_vii:N , @@ -761,8 +740,7 @@ % \@@_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, -% dispatched using \cs{@@_type_from_scan:N}. +% 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 @@ -785,23 +763,35 @@ % because \tn{protect} is often \cs{scan_stop:} hence \enquote{does % not exist}. % \begin{macrocode} -\cs_new:Npn \@@_parse_one_fp:NN #1#2 +\cs_new:Npn \@@_parse_one_fp:NN #1 { - \cs:w @@_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end: + \@@_exp_after_any_f:nw { \exp_after:wN \@@_parse_infix:NN \exp_after:wN #1 \exp:w \@@_parse_expand:w } - #2 } \cs_new:Npn \@@_exp_after_mark_f:nw #1 { - \__msg_kernel_expandable_error:nn { kernel } { fp-early-end } - \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w #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 { - \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#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> @@ -815,9 +805,9 @@ \str_if_eq:nnTF {##2} { \protect } { \cs_if_eq:NNTF ##2 #1 { \use_i:nn } { \use:n } - { \__msg_kernel_expandable_error:nnn { kernel } { fp-robust-cmd } } + { \__kernel_msg_expandable_error:nnn { kernel } { fp-robust-cmd } } } - { \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {##2} } + { \__kernel_msg_expandable_error:nnn { kernel } { bad-variable } {##2} } } } } @@ -996,7 +986,7 @@ { \cs_if_exist_use:cF { @@_parse_caseless_ \str_fold_case:n {#2} :N } { - \__msg_kernel_expandable_error:nnn + \__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 @@ -1051,13 +1041,13 @@ { \cs_if_exist:cTF { @@_parse_infix_ \token_to_str:N #1 :N } { - \__msg_kernel_expandable_error:nnn + \__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 } { - \__msg_kernel_expandable_error:nnn + \__kernel_msg_expandable_error:nnn { kernel } { fp-unknown-symbol } {#1} \@@_parse_one:Nw #3 } @@ -1729,12 +1719,12 @@ \__str_if_eq_x:nn { \s_@@ } { \exp_not:N #1 } = 0 \exp_stop_f: 0 - \__msg_kernel_expandable_error:nnn + \__kernel_msg_expandable_error:nnn { kernel } { fp-after-e } { floating~point~ } \prg_return_true: \else: 0 - \__msg_kernel_expandable_error:nnn + \__kernel_msg_expandable_error:nnn { kernel } { bad-variable } {#1} \prg_return_false: \fi: @@ -1745,14 +1735,14 @@ \__int_value:w #1 \else: 0 - \__msg_kernel_expandable_error:nnn + \__kernel_msg_expandable_error:nnn { kernel } { fp-after-e } { dimension~#1 } \fi: \prg_return_false: \fi: \else: 0 - \__msg_kernel_expandable_error:nnn + \__kernel_msg_expandable_error:nnn { kernel } { fp-missing } { exponent } \prg_return_true: \fi: @@ -1771,23 +1761,71 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[EXP]{\@@_parse_apply_unary:NNNwN} +% \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} % -% This is redefined in \pkg{l3fp-extras}. +% \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 @@ -1840,13 +1878,15 @@ % \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. Commas are allowed if -% the previous precedence is $16$ (function with multiple arguments). -% In this case, find an -% operand using the precedence~$1$; otherwise the precedence~$0$. +% 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 the array it found as an operand, +% and leaves in the input stream an operand, % fetching the following infix operator. % \begin{macrocode} \cs_new:cpn { @@_parse_prefix_(:Nw } #1 @@ -1854,10 +1894,10 @@ \exp_after:wN \@@_parse_lparen_after:NwN \exp_after:wN #1 \exp:w - \if_int_compare:w #1 = \c_@@_prec_funcii_int + \if_int_compare:w #1 = \c_@@_prec_func_int \@@_parse_operand:Nw \c_@@_prec_comma_int \else: - \@@_parse_operand:Nw \c_@@_prec_paren_int + \@@_parse_operand:Nw \c_@@_prec_tuple_int \fi: \@@_parse_expand:w } @@ -1874,8 +1914,9 @@ \exp_not:N \@@_parse_expand:w } { - \exp_not:N \__msg_kernel_expandable_error:nnn + \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 } @@ -1884,18 +1925,23 @@ % \end{macro} % % \begin{macro}[EXP]{\@@_parse_prefix_):Nw} -% The right parenthesis can appear as unary prefixes when arguments of -% a multi-argument function end with a comma, or when there is no -% argument, as in |max(1,2,)| or in |rand()|. In single-argument -% functions (precedence~$0$ rather than~$1$) forbid this. +% 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: - \__msg_kernel_expandable_error:nnn - { kernel } { fp-missing-number } { ) } - \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w + \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:NN #1 ) } @@ -2011,12 +2057,12 @@ } \cs_new:Npn \@@_parse_function:NNN #1#2#3 { - \exp_after:wN \@@_parse_apply_unary:NNNwN + \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_funcii_int \@@_parse_expand:w + \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w } % \end{macrocode} % \end{macro} @@ -2086,18 +2132,79 @@ % \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|. +% 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 +\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 \cs:w @@_#3_o:ww \cs_end: #2 #4 - \exp:w \exp_end_continue_f:w #5 #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} @@ -2111,40 +2218,40 @@ \@@_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_x: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_juxtapose:N - \fi: +\cs_new:Npn \@@_parse_infix:NN #1 #2 + { + \if_catcode:w \scan_stop: \exp_not:N #2 + \if_int_compare:w + \__str_if_eq_x: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: - \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_juxtapose: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: + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_parse_infix_mul:N \fi: - #1 - #2 - } + \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 + } \cs_new:Npx \@@_parse_infix_check:NNN #1#2#3 { \exp_not:N \if_meaning:w \scan_stop: #1 - \exp_not:N \__msg_kernel_expandable_error:nnn + \exp_not:N \__kernel_msg_expandable_error:nnn { kernel } { fp-missing } { * } \exp_not:N \exp_after:wN \exp_not:c { @@_parse_infix_*:N } @@ -2185,21 +2292,21 @@ % \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. +% 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_paren_int - \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) } - \exp_after:wN \@@_parse_infix:NN - \exp_after:wN ##1 - \exp:w \exp_after:wN \@@_parse_expand:w - \else: + \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: } } @@ -2208,10 +2315,17 @@ % \end{macro} % % \begin{macro}[verb, EXP]{\__fp_parse_infix_,:N} -% \begin{macro}[EXP]{\@@_parse_infix_comma:w, \@@_parse_infix_comma_error:w} -% \begin{macro}[verb, EXP]{\__fp_,_o:ww} -% \cs{@@_,_o:ww} is a complicated way of replacing any number of -% floating point arguments by \texttt{nan}. +% \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 { @@ -2223,10 +2337,15 @@ \exp_after:wN #1 \else: \if_int_compare:w ##1 < \c_@@_prec_comma_int - \@@_parse_infix_comma_error:w + \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: - \exp_after:wN \@@_parse_infix_comma:w - \exp:w \@@_parse_operand:Nw \c_@@_prec_comma_int + \@@_parse_operand:Nw \c_@@_prec_comma_int \exp_after:wN \@@_parse_expand:w \fi: } @@ -2234,32 +2353,18 @@ \exp_args:Nc \@@_tmp:w { @@_parse_infix_,:N } \cs_new:Npn \@@_parse_infix_comma:w #1 @ { #1 @ \use_none:n } -\cs_new:Npn \@@_parse_infix_comma_error:w #1 \exp:w - { - \fi: - \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma } - \exp_after:wN @ - \exp_after:wN \@@_parse_apply_binary:NwNwN - \exp_after:wN , - \exp:w - } -\cs_set_protected:Npn \@@_tmp:w #1 +\cs_new:Npn \@@_parse_apply_comma:NwNwN #1 #2@ #3 #4@ #5 { - \cs_new:Npn #1 ##1 - { - \if_meaning:w \s_@@ ##1 - \exp_after:wN \@@_use_i_until_s:nw - \exp_after:wN #1 - \fi: - \exp_after:wN \c_nan_fp - ##1 - } + \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 } -\exp_args:Nc \@@_tmp:w { @@_,_o:ww } % \end{macrocode} % \end{macro} % \end{macro} -% \end{macro} % % \subsubsection{Usual infix operators} % @@ -2318,48 +2423,10 @@ % \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_juxtapose:N}. +% contents of the parentheses using \cs{@@_parse_infix_mul:N}. % \begin{macrocode} \cs_new:cpn { @@_parse_infix_(:N } #1 - { \@@_parse_infix_juxtapose:N #1 ( } -% \end{macrocode} -% \end{macro} -% -% ^^A todo: can |...(1,2,3)pt| really occur? If not, simplify. -% \begin{macro}[EXP] -% {\@@_parse_infix_juxtapose:N, \@@_parse_apply_juxtapose:NwwN} -% Juxtaposition follows the same scheme as other binary operations, -% but calls \cs{@@_parse_apply_juxtapose:NwwN} rather than directly -% calling \cs{@@_parse_apply_binary:NwNwN}. This lets us catch errors -% such as |...(1,2,3)pt| where one operand of the juxtaposition is not -% a single number: both |#3| and~|#5| of the \texttt{apply} auxiliary -% must be empty. -% \begin{macrocode} -\cs_new:Npn \@@_parse_infix_juxtapose:N #1 - { - \if_int_compare:w #1 < \c_@@_prec_times_int - \exp_after:wN @ - \exp_after:wN \@@_parse_apply_juxtapose:NwwN - \exp:w - \@@_parse_operand:Nw \c_@@_prec_times_int - \exp_after:wN \@@_parse_expand:w - \else: - \exp_after:wN @ - \exp_after:wN \use_none:n - \exp_after:wN \@@_parse_infix_juxtapose:N - \fi: - } -\cs_new:Npn \@@_parse_apply_juxtapose:NwwN #1 #2;#3@ #4;#5@ - { - \if_catcode:w ^ \tl_to_str:n { #3 #5 } ^ - \else: - \@@_error:nffn { fp-invalid-ii } - { \@@_array_to_clist:n { #2; #3 } } - { \@@_array_to_clist:n { #4; #5 } } - { } - \fi: - \@@_parse_apply_binary:NwNwN #1 #2;@ * #4;@ - } + { \@@_parse_infix_mul:N #1 ( } % \end{macrocode} % \end{macro} % @@ -2436,7 +2503,7 @@ \exp_args:Nc \@@_tmp:w { @@_parse_infix_::N } \@@_ternary_auxii:NwwN \c_@@_prec_colon_int { - \__msg_kernel_expandable_error:nnnn + \__kernel_msg_expandable_error:nnnn { kernel } { fp-missing } { ? } { ~for~?: } } % \end{macrocode} @@ -2477,7 +2544,7 @@ } \cs_new:Npn \@@_parse_excl_error: { - \__msg_kernel_expandable_error:nnnn + \__kernel_msg_expandable_error:nnnn { kernel } { fp-missing } { = } { ~after~!. } } \cs_new:Npn \@@_parse_compare:NNNNNNN #1 @@ -2570,6 +2637,142 @@ % \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} +% % \subsection{Candidate: defining new \pkg{l3fp} functions} % % \begin{macro}[EXP]{\fp_function:Nw} @@ -2582,7 +2785,7 @@ \exp_after:wN \@@_function_apply:nw \exp_after:wN #1 \exp:w - \@@_parse_operand:Nw \c_@@_prec_funcii_int \@@_parse_expand:w + \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w } % \end{macrocode} % \end{macro} @@ -2618,7 +2821,7 @@ \__int_value:w #3 \exp_after:wN ; \exp_after:wN } \exp:w - \@@_parse_operand:Nw \c_@@_prec_funcii_int \@@_parse_expand:w + \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w } \cs_new:Npn #2 #4 {#5} } @@ -2628,7 +2831,7 @@ \int_compare:nNnTF { \tl_count:n {#3} } = {#2} { #1 #3 } { - \__msg_kernel_expandable_error:nnnnn + \__kernel_msg_expandable_error:nnnnn { kernel } { fp-num-args } { #1() } {#2} {#2} \c_nan_fp } @@ -2682,30 +2885,36 @@ % \subsection{Messages} % % \begin{macrocode} -\__msg_kernel_new:nnn { kernel } { fp-deprecated } +\__kernel_msg_new:nnn { kernel } { fp-deprecated } { '#1'~deprecated;~use~'#2' } -\__msg_kernel_new:nnn { kernel } { unknown-fp-word } +\__kernel_msg_new:nnn { kernel } { unknown-fp-word } { Unknown~fp~word~#1. } -\__msg_kernel_new:nnn { kernel } { fp-missing } +\__kernel_msg_new:nnn { kernel } { fp-missing } { Missing~#1~inserted #2. } -\__msg_kernel_new:nnn { kernel } { fp-extra } +\__kernel_msg_new:nnn { kernel } { fp-extra } { Extra~#1~ignored. } -\__msg_kernel_new:nnn { kernel } { fp-early-end } +\__kernel_msg_new:nnn { kernel } { fp-early-end } { Premature~end~in~fp~expression. } -\__msg_kernel_new:nnn { kernel } { fp-after-e } +\__kernel_msg_new:nnn { kernel } { fp-after-e } { Cannot~use~#1 after~'e'. } -\__msg_kernel_new:nnn { kernel } { fp-missing-number } +\__kernel_msg_new:nnn { kernel } { fp-missing-number } { Missing~number~before~'#1'. } -\__msg_kernel_new:nnn { kernel } { fp-unknown-symbol } +\__kernel_msg_new:nnn { kernel } { fp-unknown-symbol } { Unknown~symbol~#1~ignored. } -\__msg_kernel_new:nnn { kernel } { fp-extra-comma } - { Unexpected~comma:~extra~arguments~ignored. } -\__msg_kernel_new:nnn { kernel } { fp-num-args } +\__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. } %<*package> \cs_if_exist:cT { @unexpandable@protect } { - \__msg_kernel_new:nnn { kernel } { fp-robust-cmd } + \__kernel_msg_new:nnn { kernel } { fp-robust-cmd } { Robust~command~#1 invalid~in~fp~expression! } } %</package> |