summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx667
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>