summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3fp-parse.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3kernel/l3fp-parse.dtx
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3fp-parse.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-parse.dtx2908
1 files changed, 2908 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/l3fp-parse.dtx b/macros/latex/contrib/l3kernel/l3fp-parse.dtx
new file mode 100644
index 0000000000..b3d7b32bc8
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-parse.dtx
@@ -0,0 +1,2908 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-parse.dtx
+%
+% Copyright (C) 2011-2019 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version. The latest version
+% of this license is in the file
+%
+% https://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \textsf{l3fp-parse} package\\
+% Floating point expression parsing}
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+% \date{Released 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-parse} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \subsection{Work plan}
+%
+% The task at hand is non-trivial, and some previous failed attempts
+% show that the code leads to unreadable logs, so we had better get it
+% (almost) right the first time. Let us first describe our goal, then
+% discuss the design precisely before writing any code.
+%
+% In this file at least, a \meta{floating point object} is a floating
+% point number or tuple. This can be extended to anything that starts
+% with \cs{s_@@} or \cs{s_@@_\meta{type}} and ends with |;| with some
+% internal structure that depends on the \meta{type}.
+%
+% \begin{macro}[EXP]{\@@_parse:n}
+% \begin{syntax}
+% \cs{@@_parse:n} \Arg{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{floating point expression} and leaves the result
+% in the input stream as a floating point object. This
+% function forms the basis of almost all public \pkg{l3fp} functions.
+% During evaluation, each token is fully \texttt{f}-expanded.
+%
+% \cs{@@_parse_o:n} does the same but expands once after its result.
+% \begin{texnote}
+% Registers (integers, toks, etc.) are automatically unpacked,
+% without requiring a function such as \cs{int_use:N}. Invalid
+% tokens remaining after \texttt{f}-expansion lead to
+% unrecoverable low-level \TeX{} errors.
+% \end{texnote}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_prec_func_int,
+% \c_@@_prec_hatii_int,
+% \c_@@_prec_hat_int,
+% \c_@@_prec_not_int,
+% \c_@@_prec_juxt_int,
+% \c_@@_prec_times_int,
+% \c_@@_prec_plus_int,
+% \c_@@_prec_comp_int,
+% \c_@@_prec_and_int,
+% \c_@@_prec_or_int,
+% \c_@@_prec_quest_int,
+% \c_@@_prec_colon_int,
+% \c_@@_prec_comma_int,
+% \c_@@_prec_tuple_int,
+% \c_@@_prec_end_int,
+% }
+% Floating point expressions are composed of numbers, given in various
+% forms, infix operators, such as |+|, |**|, or~|,| (which joins two
+% numbers into a list), and prefix operators, such as the unary~|-|,
+% functions, or opening parentheses. Here is a list of precedences
+% which control the order of evaluation (some distinctions are
+% irrelevant for the order of evaluation, but serve as signals), from
+% the tightest binding to the loosest binding.
+% \begin{itemize}
+% \item[16] Function calls.
+% \item[13/14] Binary |**| and~|^| (right to left).
+% \item[12] Unary |+|, |-|, |!| (right to left).
+% \item[11] Juxtaposition (implicit~|*|) with no parenthesis.
+% \item[10] Binary |*| and~|/|.
+% \item[9] Binary |+| and~|-|.
+% \item[7] Comparisons.
+% \item[6] Logical \texttt{and}, denoted by~|&&|.
+% \item[5] Logical \texttt{or}, denoted by~\verb*+||+.
+% \item[4] Ternary operator |?:|, piece~|?|.
+% \item[3] Ternary operator |?:|, piece~|:|.
+% \item[2] Commas.
+% \item[1] Place where a comma is allowed and generates a tuple.
+% \item[0] Start and end of the expression.
+% \end{itemize}
+% \begin{macrocode}
+\int_const:Nn \c_@@_prec_func_int { 16 }
+\int_const:Nn \c_@@_prec_hatii_int { 14 }
+\int_const:Nn \c_@@_prec_hat_int { 13 }
+\int_const:Nn \c_@@_prec_not_int { 12 }
+\int_const:Nn \c_@@_prec_juxt_int { 11 }
+\int_const:Nn \c_@@_prec_times_int { 10 }
+\int_const:Nn \c_@@_prec_plus_int { 9 }
+\int_const:Nn \c_@@_prec_comp_int { 7 }
+\int_const:Nn \c_@@_prec_and_int { 6 }
+\int_const:Nn \c_@@_prec_or_int { 5 }
+\int_const:Nn \c_@@_prec_quest_int { 4 }
+\int_const:Nn \c_@@_prec_colon_int { 3 }
+\int_const:Nn \c_@@_prec_comma_int { 2 }
+\int_const:Nn \c_@@_prec_tuple_int { 1 }
+\int_const:Nn \c_@@_prec_end_int { 0 }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Storing results}
+%
+% The main question in parsing expressions expandably is to decide where
+% to put the intermediate results computed for various subexpressions.
+%
+% One option is to store the values at the start of the expression, and
+% carry them together as the first argument of each macro. However, we
+% want to \texttt{f}-expand tokens one by one in the expression (as
+% \cs{int_eval:n} does), and with this approach, expanding the next
+% unread token forces us to jump with \cs{exp_after:wN} over every value
+% computed earlier in the expression. With this approach, the run-time
+% grows at least quadratically in the length of the expression, if
+% not as its cube (inserting the \cs{exp_after:wN} is tricky and slow).
+%
+% A second option is to place those values at the end of the expression.
+% Then expanding the next unread token is straightforward, but this
+% still hits a performance issue: for long expressions we would be
+% reaching all the way to the end of the expression at every step of the
+% calculation. The run-time is again quadratic.
+%
+% A variation of the above attempts to place the intermediate results
+% which appear when computing a parenthesized expression near the
+% closing parenthesis. This still lets us expand tokens as we go, and
+% avoids performance problems as long as there are enough parentheses.
+% However, it would be better to avoid requiring the closing
+% parenthesis to be present as soon as the corresponding opening
+% parenthesis is read: the closing parenthesis may still be hidden in a
+% macro yet to be expanded.
+%
+% Hence, we need to go for some fine expansion control: the result is
+% stored \emph{before} the start!
+%
+% Let us illustrate this idea in a simple model: adding positive
+% integers which may be resulting from the expansion of macros, or may
+% be values of registers. Assume that one number, say, $12345$, has
+% already been found, and that we want to parse the next number. The
+% current status of the code may look as follows.
+% \begin{syntax}
+% \cs{exp_after:wN} |\add:ww| \cs{int_value:w} 12345 \cs{exp_after:wN} ;
+% \cs{exp:w} |\operand:w| \meta{stuff}
+% \end{syntax}
+% One step of expansion expands \cs{exp_after:wN}, which triggers the
+% primitive \cs{int_value:w}, which reads the five digits we have
+% already found, |12345|. This integer is unfinished, causing the
+% second \cs{exp_after:wN} to expand, and to trigger the construction
+% \cs{exp:w}, which expands |\operand:w|, defined to read
+% what follows and make a number out of it, then leave \cs{exp_end:}, the
+% number, and a semicolon in the input stream. Once |\operand:w| is
+% done expanding, we obtain essentially
+% \begin{syntax}
+% \cs{exp_after:wN} |\add:ww| \cs{int_value:w} 12345 ;
+% \cs{exp:w} \cs{exp_end:} 333444 ;
+% \end{syntax}
+% where in fact \cs{exp_after:wN} has already been expanded,
+% \cs{int_value:w} has already seen |12345|, and
+% \cs{exp:w} is still looking for a number. It finds
+% \cs{exp_end:}, hence expands to nothing. Now, \cs{int_value:w} sees
+% the \texttt{;}, which cannot be part of a number. The expansion
+% stops, and we are left with
+% \begin{syntax}
+% |\add:ww| 12345 ; 333444 ;
+% \end{syntax}
+% which can safely perform the addition by grabbing two arguments
+% delimited by~|;|.
+%
+% If we were to continue parsing the expression, then the following
+% number should also be cleaned up before the next use of a binary
+% operation such as |\add:ww|. Just like \cs{int_value:w} |12345|
+% \cs{exp_after:wN}~|;| expanded what follows once, we need |\add:ww|
+% to do the calculation, and in the process to expand the following
+% once. This is also true in our real application: all the functions of
+% the form \cs[no-index]{@@_\ldots_o:ww} expand what follows once. This comes at the
+% cost of leaving tokens in the input stack, and we need to be
+% careful not to waste this memory. All of our discussion above is nice
+% but simplistic, as operations should not simply be performed in the
+% order they appear.
+%
+% \subsubsection{Precedence and infix operators}
+%
+% The various operators we will encounter have different precedences,
+% which influence the order of calculations: $1+2\times 3 = 1+(2\times
+% 3)$ because $\times$~has a higher precedence than~$+$. The true
+% analog of our macro |\operand:w| must thus take care of that. When
+% looking for an operand, it needs to perform calculations until
+% reaching an operator which has lower precedence than the one which
+% called |\operand:w|. This means that |\operand:w| must know what the
+% previous binary operator is, or rather, its precedence: we thus rename
+% it |\operand:Nw|. Let us describe as an example how we plan to do
+% the calculation |41-2^3*4+5|. More precisely we describe how to
+% perform the first operation in this expression. Here, we abuse
+% notations: the first argument of |\operand:Nw| should be an integer
+% constant (\cs{c_@@_prec_plus_int}, \ldots{}) equal to the precedence
+% of the given operator, not directly the operator itself.
+% \begin{itemize}
+% \item Clean up~|41| and find~|-|. We call |\operand:Nw|~|-| to find
+% the second operand.
+% \item Clean up~|2| and find~|^|.
+% \item Compare the precedences of |-| and~|^|. Since the latter is
+% higher, we need to compute the exponentiation. For this, find the
+% second operand with a nested call to |\operand:Nw|~|^|.
+% \item Clean up~|3| and find~|*|.
+% \item Compare the precedences of |^| and~|*|. Since the former is
+% higher, |\operand:Nw|~|^| has found the second operand of the
+% exponentiation, which is computed: $2^{3} = 8$.
+% \item We now have |41-8*4+5|, and |\operand:Nw|~|-| is still
+% looking for a second operand for the subtraction. Is it~$8$?
+% \item Compare the precedences of |-| and~|*|. Since the latter is
+% higher, we are not done with~$8$. Call |\operand:Nw|~|*| to find
+% the second operand of the multiplication.
+% \item Clean up~|4|, and find~|+|.
+% \item Compare the precedences of |*| and~|+|. Since the former is
+% higher, |\operand:Nw|~|*| has found the second operand of the
+% multiplication, which is computed: $8*4 = 32$.
+% \item We now have |41-32+5|, and |\operand:Nw|~|-| is still looking
+% for a second operand for the subtraction. Is it~$32$?
+% \item Compare the precedences of |-| and~|+|. Since they are equal,
+% |\operand:Nw|~|-| has found the second operand for the
+% subtraction, which is computed: $41-32=9$.
+% \item We now have |9+5|.
+% \end{itemize}
+% The procedure above stops short of performing all computations, but
+% adding a surrounding call to |\operand:Nw| with a very low precedence
+% ensures that all computations are performed before |\operand:Nw|
+% is done. Adding a trailing marker with the same very low precedence
+% prevents the surrounding |\operand:Nw| from going beyond the marker.
+%
+% The pattern above to find an operand for a given operator, is to find
+% one number and the next operator, then compare precedences to know if
+% the next computation should be done. If it should, then perform it
+% after finding its second operand, and look at the next operator, then
+% compare precedences to know if the next computation should be done.
+% This continues until we find that the next computation should not be
+% done. Then, we stop.
+%
+% We are now ready to get a bit more technical and describe which of the
+% \pkg{l3fp-parse} functions correspond to each step above.
+%
+% First, \cs{@@_parse_operand:Nw} is the |\operand:Nw| function above,
+% with small modifications due to expansion issues discussed later. We
+% denote by \meta{precedence} the argument of \cs{@@_parse_operand:Nw},
+% that is, the precedence of the binary operator whose operand we are
+% trying to find. The basic action is to read numbers from the input
+% stream. This is done by \cs{@@_parse_one:Nw}. A first approximation
+% of this function is that it reads one \meta{number}, performing no
+% computation, and finds the following binary \meta{operator}. Then it
+% expands to
+% \begin{quote}
+% \meta{number}\\
+% | \__fp_parse_infix_|\meta{operator}|:N| \meta{precedence}
+% \end{quote}
+% expanding the \texttt{infix} auxiliary before leaving the above in the
+% input stream.
+%
+% We now explain the \texttt{infix} auxiliaries. We need some
+% flexibility in how we treat the case of equal precedences: most often,
+% the first operation encountered should be performed, such as |1-2-3|
+% being computed as |(1-2)-3|, but |2^3^4| should be evaluated as
+% |2^(3^4)| instead. For this reason, and to support the equivalence
+% between |**| and~|^| more easily, each binary operator is converted to
+% a control sequence |\__fp_parse_infix_|\meta{operator}|:N| when it is
+% encountered for the first time. Instead of passing both precedences
+% to a test function to do the comparison steps above, we pass the
+% \meta{precedence} (of the earlier operator) to the \texttt{infix}
+% auxiliary for the following \meta{operator}, to know whether to
+% perform the computation of the \meta{operator}. If it should not be
+% performed, the \texttt{infix} auxiliary expands to
+% \begin{syntax}
+% |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% and otherwise it calls \cs{@@_parse_operand:Nw} with the precedence of
+% the \meta{operator} to find its second operand \meta{number_2} and the
+% next \meta{operator_2}, and expands to
+% \begin{syntax}
+% |@| \cs{@@_parse_apply_binary:NwNwN}
+% ~~~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% The \texttt{infix} function is responsible for comparing precedences,
+% but cannot directly call the computation functions, because the first
+% operand \meta{number} is before the \texttt{infix} function in the
+% input stream. This is why we stop the expansion here and give control
+% to another function to close the loop.
+%
+% A definition of \cs{@@_parse_operand:Nw} \meta{precedence} with some
+% of the expansion control removed is
+% \begin{syntax}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN}
+% \cs{exp_after:wN} \meta{precedence}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% ~~\cs{@@_parse_one:Nw} \meta{precedence}
+% \end{syntax}
+% This expands \cs{@@_parse_one:Nw} \meta{precedence} completely, which
+% finds a number, wraps the next \meta{operator} into an \texttt{infix}
+% function, feeds this function the \meta{precedence}, and expands it,
+% yielding either
+% \begin{syntax}
+% \cs{@@_parse_continue:NwN} \meta{precedence}
+% \meta{number} |@|
+% \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% or
+% \begin{syntax}
+% \cs{@@_parse_continue:NwN} \meta{precedence}
+% \meta{number} |@|
+% \cs{@@_parse_apply_binary:NwNwN}
+% ~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% The definition of \cs{@@_parse_continue:NwN} is then very simple:
+% \begin{syntax}
+% |\cs_new:Npn \__fp_parse_continue:NwN #1#2@#3 { #3 #1 #2 @ }|
+% \end{syntax}
+% In the first case, |#3|~is \cs{use_none:n}, yielding
+% \begin{syntax}
+% \cs{use_none:n} \meta{precedence} \meta{number} |@|
+% |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{syntax}
+% then \meta{number} |@| |\__fp_parse_infix_|\meta{operator}|:N|. In
+% the second case, |#3|~is \cs{@@_parse_apply_binary:NwNwN}, whose role
+% is to compute \meta{number} \meta{operator} \meta{number_2} and to
+% prepare for the next comparison of precedences: first we get
+% \begin{syntax}
+% \cs{@@_parse_apply_binary:NwNwN}
+% ~~\meta{precedence} \meta{number} |@|
+% ~~\meta{operator} \meta{number_2}
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{syntax}
+% then
+% \begin{syntax}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN}
+% \cs{exp_after:wN} \meta{precedence}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% |\__fp_|\meta{operator}|_o:ww| \meta{number} \meta{number_2}
+% \cs{exp:w} \cs{exp_end_continue_f:w}
+% |\__fp_parse_infix_|\meta{operator_2}|:N| \meta{precedence}
+% \end{syntax}
+% where |\__fp_|\meta{operator}|_o:ww| computes \meta{number}
+% \meta{operator} \meta{number_2} and expands after the result, thus
+% triggers the comparison of the precedence of the \meta{operator_2} and
+% the \meta{precedence}, continuing the loop.
+%
+% We have introduced the most important functions here, and the next few
+% paragraphs we describe various subtleties.
+%
+% \subsubsection{Prefix operators, parentheses, and functions}
+%
+% Prefix operators (unary |-|, |+|,~|!|) and parentheses are taken care
+% of by the same mechanism, and functions (\texttt{sin}, \texttt{exp},
+% etc.) as well. Finding the argument of the unary~|-|, for instance,
+% is very similar to grabbing the second operand of a binary infix
+% operator, with a subtle precedence explained below. Once that operand
+% is found, the operator can be applied to it (for the unary~|-|, this
+% simply flips the sign). A left parenthesis is just a prefix operator
+% with a very low precedence equal to that of the closing parenthesis
+% (which is treated as an infix operator, since it normally appears just
+% after numbers), so that all computations are performed until the
+% closing parenthesis. The prefix operator associated to the left
+% parenthesis does not alter its argument, but it removes the closing
+% parenthesis (with some checks).
+%
+% Prefix operators are the reason why we only summarily described the
+% function \cs{@@_parse_one:Nw} earlier. This function is responsible
+% for reading in the input stream the first possible \meta{number} and
+% the next infix \meta{operator}. If what follows \cs{@@_parse_one:Nw}
+% \meta{precedence} is a prefix operator, then we must find the operand
+% of this prefix operator through a nested call to
+% \cs{@@_parse_operand:Nw} with the appropriate precedence, then apply
+% the operator to the operand found to yield the result of
+% \cs{@@_parse_one:Nw}. So far, all is simple.
+%
+% The unary operators |+|, |-|,~|!| complicate things a little bit:
+% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. This would easily
+% be done by giving~|-| a lower precedence, equal to that of the infix
+% |+| and~|-|. Unfortunately, this fails in cases such as |3**-2*4|,
+% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. A
+% second attempt would be to call \cs{@@_parse_operand:Nw} with the
+% \meta{precedence} of the previous operator, but |0>-2+3| is then
+% parsed as |0>-(2+3)|: the addition is performed because it binds more
+% tightly than the comparision which precedes~|-|. The correct approach
+% is for a unary~|-| to perform operations whose precedence is greater
+% than both that of the previous operation, and that of the unary~|-|
+% itself. The unary~|-| is given a precedence higher than
+% multiplication and division. This does not lead to any surprising
+% result, since $-(x/y) = (-x)/y$ and similarly for multiplication, and
+% it reduces the number of nested calls to \cs{@@_parse_operand:Nw}.
+%
+% Functions are implemented as prefix operators with very high
+% precedence, so that their argument is the first number that can
+% possibly be built.
+%
+% Note that contrarily to the \texttt{infix} functions discussed
+% earlier, the \texttt{prefix} functions do perform tests on the
+% previous \meta{precedence} to decide whether to find an argument or
+% not, since we know that we need a number, and must never stop there.
+%
+% \subsubsection{Numbers and reading tokens one by one}
+%
+% So far, we have glossed over one important point: what is a
+% \enquote{number}? A number is typically given in the form
+% \meta{significand}|e|\meta{exponent}, where the \meta{significand} is
+% any non-empty string composed of decimal digits and at most one
+% decimal separator (a period), the exponent
+% \enquote{\texttt{e}\meta{exponent}} is optional and is composed of an
+% exponent mark~|e| followed by a possibly empty string of signs
+% |+| or~|-| and a non-empty string of decimal digits. The
+% \meta{significand} can also be an integer, dimension, skip, or muskip
+% variable, in which case dimensions are converted from points (or mu
+% units) to floating points, and the \meta{exponent} can also be an
+% integer variable. Numbers can also be given as floating point
+% variables, or as named constants such as |nan|, |inf| or~|pi|. We may
+% add more types in the future.
+%
+% When \cs{@@_parse_one:Nw} is looking for a \enquote{number}, here is
+% what happens.
+% \begin{itemize}
+% \item If the next token is a control sequence with the meaning of
+% \cs{scan_stop:}, it can be: \cs{s_@@}, in which case our job is
+% done, as what follows is an internal floating point number, or
+% \cs{s_@@_mark}, in which case the expression has come to an early
+% end, as we are still looking for a number here, or something else,
+% in which case we consider the control sequence to be a bad
+% variable resulting from \texttt{c}-expansion.
+% \item If the next token is a control sequence with a different
+% meaning, we assume that it is a register, unpack it with
+% \cs{tex_the:D}, and use its value (in \texttt{pt} for dimensions
+% and skips, \texttt{mu} for muskips) as the \meta{significand} of a
+% number: we look for an exponent.
+% \item If the next token is a digit, we remove any leading zeros,
+% then read a significand larger than~$1$ if the next character is a
+% digit, read a significand smaller than~$1$ if the next character
+% is a period, or we have found a significand equal to~$0$
+% otherwise, and look for an exponent.
+% \item If the next token is a letter, we collect more letters until
+% the first non-letter: the resulting word may denote a function
+% such as |asin|, a constant such as |pi| or be unknown. In the
+% first case, we call \cs{@@_parse_operand:Nw} to find the argument
+% of the function, then apply the function, before declaring that we
+% are done. Otherwise, we are done, either with the value of the
+% constant, or with the value |nan| for unknown words.
+% \item If the next token is anything else, we check whether it is a
+% known prefix operator, in which case \cs{@@_parse_operand:Nw}
+% finds its operand. If it is not known, then either a number is
+% missing (if the token is a known infix operator) or the token is
+% simply invalid in floating point expressions.
+% \end{itemize}
+% Once a number is found, \cs{@@_parse_one:Nw} also finds an infix
+% operator. This goes as follows.
+% \begin{itemize}
+% \item If the next token is a control sequence, it could be the
+% special marker \cs{s_@@_mark}, and
+% otherwise it is a case of juxtaposing numbers, such as
+% |2\c_zero_int|, with an implied multiplication.
+% \item If the next token is a letter, it is also a case of
+% juxtaposition, as letters cannot be proper infix operators.
+% \item Otherwise (including in the case of digits), if the token is a
+% known infix operator, the appropriate
+% |\__fp_infix_|\meta{operator}|:N| function is built, and if it
+% does not exist, we complain. In particular, the juxtaposition
+% |\c_zero_int 2| is disallowed.
+% \end{itemize}
+%
+% In the above, we need to test whether a character token~|#1| is a
+% digit:
+% \begin{verbatim}
+% \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+% is a digit
+% \else:
+% not a digit
+% \fi:
+% \end{verbatim}
+% To exclude |0|, replace |9| by |10|. The use of
+% \cs{token_to_str:N} ensures that a digit with any catcode is detected.
+% To test if a character token is a letter, we need to work with its
+% character code, testing if |`#1| lies in $[65,90]$ (uppercase letters)
+% or $[97,112]$ (lowercase letters)
+% \begin{verbatim}
+% \if_int_compare:w \__fp_int_eval:w
+% ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26 = 3 \exp_stop_f:
+% is a letter
+% \else:
+% not a letter
+% \fi:
+% \end{verbatim}
+% At all steps, we try to accept all category codes: when |#1|~is kept
+% to be used later, it is almost always converted to category code other
+% through \cs{token_to_str:N}. More precisely, catcodes $\{3, 6, 7, 8,
+% 11, 12\}$ should work without trouble, but not $\{1, 2, 4, 10, 13\}$,
+% and of course $\{0, 5, 9\}$ cannot become tokens.
+%
+% Floating point expressions should behave as much as possible like
+% \eTeX{}-based integer expressions and dimension expressions. In
+% particular, \texttt{f}-expansion should be performed as the expression
+% is read, token by token, forcing the expansion of protected macros,
+% and ignoring spaces. One advantage of expanding at every step is that
+% restricted expandable functions can then be used in floating point
+% expressions just as they can be in other kinds of expressions.
+% Problematically, spaces stop \texttt{f}-expansion: for instance, the
+% macro~|\X| below would not be expanded if we simply performed
+% \texttt{f}-expansion.
+% \begin{verbatim}
+% \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} }
+% \ExplSyntaxOff
+% \test { 1 + \X }
+% \end{verbatim}
+% Of course, spaces typically do not appear in a code setting, but may very
+% easily come in document-level input, from which some expressions may
+% come. To avoid this problem, at every step, we do essentially what
+% \cs{use:f} would do: take an argument, put it back in the input
+% stream, then \texttt{f}-expand it. This is not a complete solution,
+% since a macro's expansion could contain leading spaces which would stop
+% the \texttt{f}-expansion before further macro calls are performed.
+% However, in practice it should be enough: in particular, floating
+% point numbers are correctly expanded to the underlying \cs{s_@@}
+% \ldots{} structure. The \texttt{f}-expansion is performed by
+% \cs{@@_parse_expand:w}.
+%
+% ^^A begin[todo]
+%
+% \subsection{Main auxiliary functions}
+%
+% \begin{macro}[rEXP]{\@@_parse_operand:Nw}
+% \begin{syntax}
+% \cs{exp:w} \cs{@@_parse_operand:Nw} \meta{precedence} \cs{@@_parse_expand:w}
+% \end{syntax}
+% Reads the \enquote{\ttfamily\ldots{}}, performing every computation
+% with a precedence higher than \meta{precedence}, then expands to
+% \begin{syntax}
+% \meta{result} |@| |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
+% \end{syntax}
+% where the \meta{operation} is the first operation with a lower
+% precedence, possibly \texttt{end}, and the
+% \enquote{\ttfamily\ldots{}} start just after the \meta{operation}.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_+:N}
+% \begin{syntax}
+% \cs{@@_parse_infix_+:N} \meta{precedence} \ldots{}
+% \end{syntax}
+% If |+|~has a precedence higher than the \meta{precedence}, cleans up
+% a second \meta{operand} and finds the \meta{operation_2} which
+% follows, and expands to
+% \begin{syntax}
+% |@| \cs{@@_parse_apply_binary:NwNwN} |+| \meta{operand} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{}
+% \end{syntax}
+% Otherwise expands to
+% \begin{syntax}
+% |@| \cs{use_none:n} \cs{@@_parse_infix_+:N} \ldots{}
+% \end{syntax}
+% A similar function exists for each infix operator.
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one:Nw}
+% \begin{syntax}
+% \cs{@@_parse_one:Nw} \meta{precedence} \ldots{}
+% \end{syntax}
+% Cleans up one or two operands depending on how the precedence of the
+% next operation compares to the \meta{precedence}. If the following
+% \meta{operation} has a precedence higher than \meta{precedence},
+% expands to
+% \begin{syntax}
+% \meta{operand_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{operand_2} |@| |\__fp_parse_infix_|\meta{operation_2}|:N| \ldots{}
+% \end{syntax}
+% and otherwise expands to
+% \begin{syntax}
+% \meta{operand} |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
+% \end{syntax}
+% \end{macro}
+%
+% ^^A end[todo]
+%
+% \subsection{Helpers}
+%
+% \begin{macro}[rEXP]{\@@_parse_expand:w}
+% \begin{syntax}
+% \cs{exp:w} \cs{@@_parse_expand:w} \meta{tokens}
+% \end{syntax}
+% This function must always come within a \cs{exp:w} expansion.
+% The \meta{tokens} should be the part of the expression that we have
+% not yet read. This requires in particular closing all conditionals
+% properly before expanding.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_expand:w #1 { \exp_end_continue_f:w #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_return_semicolon:w}
+% This very odd function swaps its position with the following
+% \cs{fi:} and removes \cs{@@_parse_expand:w} normally responsible for
+% expansion. That turns out to be useful.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_return_semicolon:w
+ #1 \fi: \@@_parse_expand:w { \fi: ; #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_digits_vii:N ,
+% \@@_parse_digits_vi:N ,
+% \@@_parse_digits_v:N ,
+% \@@_parse_digits_iv:N ,
+% \@@_parse_digits_iii:N ,
+% \@@_parse_digits_ii:N ,
+% \@@_parse_digits_i:N ,
+% \@@_parse_digits_:N
+% }
+% These functions must be called within an \cs{int_value:w} or
+% \cs{@@_int_eval:w} construction. The first token which follows must
+% be \texttt{f}-expanded prior to calling those functions. The
+% functions read tokens one by one, and output digits into the input
+% stream, until meeting a non-digit, or up to a number of digits equal
+% to their index. The full expansion is
+% \begin{syntax}
+% \meta{digits} |;| \meta{filling 0} |;| \meta{length}
+% \end{syntax}
+% where \meta{filling 0} is a string of zeros such that \meta{digits}
+% \meta{filling 0} has the length given by the index of the function,
+% and \meta{length} is the number of zeros in the \meta{filling 0}
+% string. Each function puts a digit into the input stream and calls
+% the next function, until we find a non-digit. We are careful to
+% pass the tested tokens through \cs{token_to_str:N} to normalize
+% their category code.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2 #3
+ {
+ \cs_new:cpn { @@_parse_digits_ #1 :N } ##1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N ##1 \exp_stop_f:
+ \token_to_str:N ##1 \exp_after:wN #2 \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #3 ##1
+ \fi:
+ \@@_parse_expand:w
+ }
+ }
+\@@_tmp:w {vii} \@@_parse_digits_vi:N { 0000000 ; 7 }
+\@@_tmp:w {vi} \@@_parse_digits_v:N { 000000 ; 6 }
+\@@_tmp:w {v} \@@_parse_digits_iv:N { 00000 ; 5 }
+\@@_tmp:w {iv} \@@_parse_digits_iii:N { 0000 ; 4 }
+\@@_tmp:w {iii} \@@_parse_digits_ii:N { 000 ; 3 }
+\@@_tmp:w {ii} \@@_parse_digits_i:N { 00 ; 2 }
+\@@_tmp:w {i} \@@_parse_digits_:N { 0 ; 1 }
+\cs_new:Npn \@@_parse_digits_:N { ; ; 0 }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Parsing one number}
+%
+% \begin{macro}[EXP]{\@@_parse_one:Nw}
+% This function finds one number, and packs the symbol which follows
+% in an \cs[no-index]{@@_parse_infix_\ldots{}} csname.
+% |#1|~is the previous \meta{precedence},
+% and |#2|~the first token of the operand. We distinguish four cases:
+% |#2|~is equal to \cs{scan_stop:} in meaning, |#2|~is a different
+% control sequence, |#2|~is a digit, and |#2|~is something else (this
+% last case is split further later). Despite the earlier
+% \texttt{f}-expansion, |#2|~may still be expandable if it was
+% protected by \cs{exp_not:N}, as may happen with the \LaTeXe{} command
+% \tn{protect}. Using a well placed \cs{reverse_if:N}, this case is
+% sent to \cs{@@_parse_one_fp:NN} which deals with it robustly.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one:Nw #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \exp_after:wN \if_meaning:w \exp_not:N #2 #2 \else:
+ \exp_after:wN \reverse_if:N
+ \fi:
+ \if_meaning:w \scan_stop: #2
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_fp:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_register:NN
+ \fi:
+ \else:
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_digit:NN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_one_other:NN
+ \fi:
+ \fi:
+ #1 #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_one_fp:NN,
+% \@@_exp_after_mark_f:nw,
+% \@@_exp_after_?_f:nw
+% }
+% This function receives a \meta{precedence} and a control sequence
+% equal to \cs{scan_stop:} in meaning. There are three cases.
+% \begin{itemize}
+% \item \cs{s_@@} starts a floating point number, and we call
+% \cs{@@_exp_after_f:nw}, which |f|-expands after the floating
+% point.
+% \item \cs{s_@@_mark} is a premature end, we call
+% \cs{@@_exp_after_mark_f:nw}, which triggers an |fp-early-end|
+% error.
+% \item For a control sequence not containing \cs[no-index]{s_@@}, we call
+% \cs{@@_exp_after_?_f:nw}, causing a |bad-variable| error.
+% \end{itemize}
+% This scheme is extensible: additional types can be added by starting
+% the variables with a scan mark of the form \cs[no-index]{s_@@_\meta{type}} and
+% defining |\__fp_exp_after_|\meta{type}|_f:nw|. In all cases, we
+% make sure that the second argument of \cs{@@_parse_infix:NN} is
+% correctly expanded.
+% A special case only enabled in \LaTeXe{} is that if \tn{protect} is
+% encountered then the error message mentions the control sequence
+% which follows it rather than \tn{protect} itself. The test for
+% \LaTeXe{} uses \tn{@unexpandable@protect} rather than \tn{protect}
+% because \tn{protect} is often \cs{scan_stop:} hence \enquote{does
+% not exist}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_fp:NN #1
+ {
+ \@@_exp_after_any_f:nw
+ {
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN #1 \exp:w \@@_parse_expand:w
+ }
+ }
+\cs_new:Npn \@@_exp_after_mark_f:nw #1
+ {
+ \int_case:nnF { \exp_after:wN \use_i:nnn \use_none:nnn #1 }
+ {
+ \c_@@_prec_comma_int { }
+ \c_@@_prec_tuple_int { }
+ \c_@@_prec_end_int
+ {
+ \exp_after:wN \c_@@_empty_tuple_fp
+ \exp:w \exp_end_continue_f:w
+ }
+ }
+ {
+ \__kernel_msg_expandable_error:nn { kernel } { fp-early-end }
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ }
+ #1
+ }
+\cs_new:cpn { @@_exp_after_?_f:nw } #1#2
+ {
+ \__kernel_msg_expandable_error:nnn { kernel } { bad-variable }
+ {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w #1
+ }
+%<*package>
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_if_exist:NT #1
+ {
+ \cs_gset:cpn { @@_exp_after_?_f:nw } ##1##2
+ {
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w ##1
+ \str_if_eq:nnTF {##2} { \protect }
+ {
+ \cs_if_eq:NNTF ##2 #1 { \use_i:nn } { \use:n }
+ {
+ \__kernel_msg_expandable_error:nnn { kernel }
+ { fp-robust-cmd }
+ }
+ }
+ {
+ \__kernel_msg_expandable_error:nnn { kernel }
+ { bad-variable } {##2}
+ }
+ }
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @unexpandable@protect }
+%</package>
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_one_register:NN,
+% \@@_parse_one_register_aux:Nw,
+% \@@_parse_one_register_auxii:wwwNw,
+% \@@_parse_one_register_int:www,
+% \@@_parse_one_register_mu:www,
+% \@@_parse_one_register_dim:ww,
+% }
+% This is called whenever~|#2| is a control sequence other than
+% \cs{scan_stop:} in meaning. We special-case \tn{wd}, \tn{ht}, \tn{dp}
+% (see later) and otherwise assume that it is a register, but
+% carefully unpack it with \cs{tex_the:D} within braces. First, we
+% find the exponent following~|#2|. Then we unpack~|#2| with
+% \cs{tex_the:D}, and the \texttt{auxii} auxiliary distinguishes
+% integer registers from dimensions/skips from muskips, according to
+% the presence of a period and/or of |pt|. For integers, simply
+% convert \meta{value}|e|\meta{exponent} to a floating point number
+% with \cs{@@_parse:n} (this is somewhat wasteful). For other
+% registers, the decimal rounding provided by \TeX{} does not
+% accurately represent the binary value that it manipulates, so we
+% extract this binary value as a number of scaled points with
+% \cs{int_value:w} \cs{dim_to_decimal_in_sp:n} |{| \meta{decimal value} |pt| |}|, and
+% use an auxiliary of \cs{dim_to_fp:n}, which performs the
+% multiplication by $2^{-16}$, correctly rounded.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_register:NN #1#2
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_parse_one_register_special:N #2
+ \exp_after:wN \@@_parse_one_register_aux:Nw
+ \exp_after:wN #2
+ \int_value:w
+ \exp_after:wN \@@_parse_exponent:N
+ \exp:w \@@_parse_expand:w
+ }
+\cs_new:Npx \@@_parse_one_register_aux:Nw #1
+ {
+ \exp_not:n
+ {
+ \exp_after:wN \use:nn
+ \exp_after:wN \@@_parse_one_register_auxii:wwwNw
+ }
+ \exp_not:N \exp_after:wN { \exp_not:N \tex_the:D #1 }
+ ; \exp_not:N \@@_parse_one_register_dim:ww
+ \tl_to_str:n { pt } ; \exp_not:N \@@_parse_one_register_mu:www
+ . \tl_to_str:n { pt } ; \exp_not:N \@@_parse_one_register_int:www
+ \exp_not:N \q_stop
+ }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_parse_one_register_auxii:wwwNw #1 . #2 }
+ { \tl_to_str:n { pt } #3 ; #4#5 \q_stop }
+ { #4 #1.#2; }
+\exp_args:Nno \use:nn
+ { \cs_new:Npn \@@_parse_one_register_mu:www #1 }
+ { \tl_to_str:n { mu } ; #2 ; }
+ { \@@_parse_one_register_dim:ww #1 ; }
+\cs_new:Npn \@@_parse_one_register_int:www #1; #2.; #3;
+ { \@@_parse:n { #1 e #3 } }
+\cs_new:Npn \@@_parse_one_register_dim:ww #1; #2;
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \int_value:w #2 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 pt } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_parse_one_register_special:N,
+% \@@_parse_one_register_math:NNw,
+% \@@_parse_one_register_wd:w,
+% \@@_parse_one_register_wd:Nw
+% }
+% The \tn{wd}, \tn{dp}, \tn{ht} primitives expect an integer argument.
+% We abuse the exponent parser to find the integer argument: simply
+% include the exponent marker~|e|. Once that \enquote{exponent} is
+% found, use \cs{tex_the:D} to find the box dimension and then copy
+% what we did for dimensions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_register_special:N #1
+ {
+ \if_meaning:w \box_wd:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_ht:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \box_dp:N #1 \@@_parse_one_register_wd:w \fi:
+ \if_meaning:w \infty #1
+ \@@_parse_one_register_math:NNw \infty #1
+ \fi:
+ \if_meaning:w \pi #1
+ \@@_parse_one_register_math:NNw \pi #1
+ \fi:
+ }
+\cs_new:Npn \@@_parse_one_register_math:NNw
+ #1#2#3#4 \@@_parse_expand:w
+ {
+ #3
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-infty-pi } {#1}
+ \c_nan_fp
+ }
+ { #4 \@@_parse_expand:w }
+ }
+\cs_new:Npn \@@_parse_one_register_wd:w
+ #1#2 \exp_after:wN #3#4 \@@_parse_expand:w
+ {
+ #1
+ \exp_after:wN \@@_parse_one_register_wd:Nw
+ #4 \@@_parse_expand:w e
+ }
+\cs_new:Npn \@@_parse_one_register_wd:Nw #1#2 ;
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { #1 #2 } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one_digit:NN}
+% A digit marks the beginning of an explicit floating point number.
+% Once the number is found, we catch the case of overflow and
+% underflow with \cs{@@_sanitize:wN}, then
+% \cs{@@_parse_infix_after_operand:NwN} expands \cs{@@_parse_infix:NN}
+% after the number we find, to wrap the following infix operator as
+% required. Finding the number itself begins by removing leading
+% zeros: further steps are described later.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_digit:NN #1
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_sanitize:wN
+ \int_value:w \@@_int_eval:w 0 \@@_parse_trim_zeros:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_one_other:NN}
+% For this function, |#2|~is a character token which is not a digit.
+% If it is an \textsc{ascii} letter, \cs{@@_parse_letters:N} beyond this one and give
+% the result to \cs{@@_parse_word:Nw}. Otherwise, the character is
+% assumed to be a prefix operator, and we build
+% |\__fp_parse_prefix_|\meta{operator}|:Nw|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_other:NN #1 #2
+ {
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \@@_parse_word:Nw
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \@@_parse_letters:N
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_prefix:NNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \cs:w
+ @@_parse_prefix_ \token_to_str:N #2 :Nw
+ \exp_after:wN
+ \cs_end:
+ \exp:w
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_word:Nw}
+% \begin{macro}[rEXP]{\@@_parse_letters:N}
+% Finding letters is a simple recursion. Once \cs{@@_parse_letters:N}
+% has done its job, we try to build a control sequence from the
+% word~|#2|. If it is a known word, then the corresponding action is
+% taken, and otherwise, we complain about an unknown word, yield
+% \cs{c_nan_fp}, and look for the following infix operator. Note that
+% the unknown word could be a mistyped function as well as a mistyped
+% constant, so there is no way to tell whether to look for arguments;
+% we do not.
+% The standard requires \enquote{inf} and \enquote{infinity} and
+% \enquote{nan} to be recognized regardless of case, but we probably
+% don't want to allow every \pkg{l3fp} word to have an arbitrary
+% mixture of lower and upper case, so we test and use a
+% differently-named control sequence.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word:Nw #1#2;
+ {
+ \cs_if_exist_use:cF { @@_parse_word_#2:N }
+ {
+ \cs_if_exist_use:cF
+ { @@_parse_caseless_ \str_fold_case:n {#2} :N }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { unknown-fp-word } {#2}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \@@_parse_infix:NN
+ }
+ }
+ #1
+ }
+\cs_new:Npn \@@_parse_letters:N #1
+ {
+ \exp_end_continue_f:w
+ \if_int_compare:w
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ 0
+ \else:
+ \@@_int_eval:w
+ ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26
+ \fi:
+ = 3 \exp_stop_f:
+ \exp_after:wN #1
+ \exp:w \exp_after:wN \@@_parse_letters:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_parse_prefix:NNN, \@@_parse_prefix_unknown:NNN}
+% For this function, |#1|~is the previous \meta{precedence}, |#2|~is
+% the operator just seen, and |#3|~is a control sequence which
+% implements the operator if it is a known operator. If this control
+% sequence is \cs{scan_stop:}, then the operator is in fact unknown.
+% Either the expression is missing a number there (if the operator is
+% valid as an infix operator), and we put \texttt{nan}, wrapping the
+% infix operator in a csname as appropriate, or the character is
+% simply invalid in floating point expressions, and we continue
+% looking for a number, starting again from \cs{@@_parse_one:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_prefix:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #3
+ \exp_after:wN \@@_parse_prefix_unknown:NNN
+ \exp_after:wN #2
+ \fi:
+ #3 #1
+ }
+\cs_new:Npn \@@_parse_prefix_unknown:NNN #1#2#3
+ {
+ \cs_if_exist:cTF { @@_parse_infix_ \token_to_str:N #1 :N }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing-number } {#1}
+ \exp_after:wN \c_nan_fp \exp:w \exp_end_continue_f:w
+ \@@_parse_infix:NN #3 #1
+ }
+ {
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-unknown-symbol } {#1}
+ \@@_parse_one:Nw #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Numbers: trimming leading zeros}
+%
+% Numbers are parsed as follows: first we trim leading zeros, then
+% if the next character is a digit, start reading a significand $\geq 1$
+% with the set of functions |\__fp_parse_large|\ldots{}; if it is a
+% period, the significand is~$<1$; and otherwise it is zero. In the
+% second case, trim additional zeros after the period, counting them for
+% an exponent shift $\meta{exp_1}<0$, then read the significand with the
+% set of functions |\__fp_parse_small|\ldots{} Once the significand is
+% read, read the exponent if |e|~is present.
+%
+% \begin{macro}[rEXP]{\@@_parse_trim_zeros:N, \@@_parse_trim_end:w}
+% This function expects an already expanded token. It removes any
+% leading zero, then distinguishes three cases: if the first non-zero
+% token is a digit, then call \cs{@@_parse_large:N} (the significand
+% is $\geq 1$); if it is |.|, then continue trimming zeros with
+% \cs{@@_parse_strim_zeros:N}; otherwise, our number is exactly zero,
+% and we call \cs{@@_parse_zero:} to take care of that case.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_trim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ \exp_after:wN \@@_parse_trim_zeros:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #1
+ \exp_after:wN \@@_parse_strim_zeros:N
+ \exp:w
+ \else:
+ \@@_parse_trim_end:w #1
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_trim_end:w #1 \fi: \fi: \@@_parse_expand:w
+ {
+ \fi:
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \@@_parse_large:N
+ \else:
+ \exp_after:wN \@@_parse_zero:
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_strim_zeros:N, \@@_parse_strim_end:w}
+% If we have removed all digits until a period (or if the body started
+% with a period), then enter the \enquote{\texttt{small_trim}} loop
+% which outputs $-1$ for each removed~$0$. Those $-1$ are added to an
+% integer expression waiting for the exponent. If the first non-zero
+% token is a digit, call \cs{@@_parse_small:N} (our significand is
+% smaller than~$1$), and otherwise, the number is an exact zero. The
+% name \texttt{strim} stands for \enquote{small trim}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_strim_zeros:N #1
+ {
+ \if:w 0 \exp_not:N #1
+ - 1
+ \exp_after:wN \@@_parse_strim_zeros:N \exp:w
+ \else:
+ \@@_parse_strim_end:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_strim_end:w #1 \fi: \@@_parse_expand:w
+ {
+ \fi:
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \exp_after:wN \@@_parse_small:N
+ \else:
+ \exp_after:wN \@@_parse_zero:
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_zero:}
+% After reading a significand of~$0$, find any exponent, then put a
+% sign of~|1| for \cs{@@_sanitize:wN}, which removes everything
+% and leaves an exact zero.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_zero:
+ {
+ \exp_after:wN ; \exp_after:wN 1
+ \int_value:w \@@_parse_exponent:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: small significand}
+%
+% \begin{macro}[rEXP]{\@@_parse_small:N}
+% This function is called after we have passed the decimal separator
+% and removed all leading zeros from the significand. It is followed
+% by a non-zero digit (with any catcode). The goal is to read up to
+% $16$ digits. But we can't do that all at once, because
+% \cs{int_value:w} (which allows us to collect digits and continue
+% expanding) can only go up to $9$ digits. Hence we grab digits in
+% two steps of $8$ digits. Since |#1| is a digit, read seven more
+% digits using \cs{@@_parse_digits_vii:N}. The \texttt{small_leading}
+% auxiliary leaves those digits in the \cs{int_value:w}, and
+% grabs some more, or stops if there are no more digits. Then the
+% \texttt{pack_leading} auxiliary puts the various parts in the
+% appropriate order for the processing further up.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small:N #1
+ {
+ \exp_after:wN \@@_parse_pack_leading:NNNNNww
+ \int_value:w \@@_int_eval:w 1 \token_to_str:N #1
+ \exp_after:wN \@@_parse_small_leading:wwNN
+ \int_value:w 1
+ \exp_after:wN \@@_parse_digits_vii:N
+ \exp:w \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_small_leading:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_small_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros}
+% \end{syntax}
+% We leave \meta{digits} \meta{zeros} in the input stream: the
+% functions used to grab digits are such that this constitutes digits
+% $1$ through~$8$ of the significand. Then prepare to pack $8$~more
+% digits, with an exponent shift of zero (this shift is used in
+% the case of a large significand). If |#4|~is a digit, leave it
+% behind for the packing function, and read $6$~more digits to reach a
+% total of $15$~digits: further digits are involved in the rounding.
+% Otherwise put $8$~zeros in to complete the significand, then look
+% for an exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w 1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \@@_parse_small_trailing:wwNN
+ \int_value:w 1
+ \exp_after:wN \@@_parse_digits_vi:N
+ \exp:w
+ \else:
+ 0000 0000 \@@_parse_exponent:Nw #4
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_small_trailing:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_small_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% Leave digits $10$ to~$15$ (arguments |#1| and |#2|) in the input
+% stream. If the \meta{next~token} is a digit, it is the $16$th
+% digit, we keep it, then the \texttt{small_round} auxiliary considers
+% this digit and all further digits to perform the rounding: the
+% function expands to nothing, to |+0| or to |+1|.
+% Otherwise, there is no $16$-th digit, so we put a~$0$, and look for
+% an exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ #1 #2
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \token_to_str:N #4
+ \exp_after:wN \@@_parse_small_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ 0 \@@_parse_exponent:Nw #4
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_pack_trailing:NNNNNNww ,
+% \@@_parse_pack_leading:NNNNNww ,
+% \@@_parse_pack_carry:w
+% }
+% Those functions are expanded after all the digits are found, we took
+% care of the rounding, as well as the exponent. The last argument is
+% the exponent. The previous five arguments are $8$~digits which we
+% pack in groups of~$4$, and the argument before that is~$1$, except
+% in the rare case where rounding lead to a carry, in which case the
+% argument is~$2$. The \texttt{trailing} function has an exponent
+% shift as its first argument, which we add to the exponent found in
+% the |e...| syntax. If the trailing digits cause a carry, the
+% integer expression for the leading digits is incremented (|+1|
+% in the code below). If the leading digits propagate this carry all
+% the way up, the function \cs{@@_parse_pack_carry:w} increments the
+% exponent, and changes the significand from |0000...| to |1000...|:
+% this is simple because such a carry can only occur to give rise to a
+% power of~$10$.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ;
+ {
+ \if_meaning:w 2 #2 + 1 \fi:
+ ; #8 + #1 ; {#3#4#5#6} {#7};
+ }
+\cs_new:Npn \@@_parse_pack_leading:NNNNNww #1 #2#3#4#5 #6; #7;
+ {
+ + #7
+ \if_meaning:w 2 #1 \@@_parse_pack_carry:w \fi:
+ ; 0 {#2#3#4#5} {#6}
+ }
+\cs_new:Npn \@@_parse_pack_carry:w \fi: ; 0 #1
+ { \fi: + 1 ; 0 {1000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: large significand}
+%
+% Parsing a significand larger than~$1$ is a little bit more difficult
+% than parsing small significands. We need to count the number of
+% digits before the decimal separator, and add that to the final
+% exponent. We also need to test for the presence of a dot each time we
+% run out of digits, and branch to the appropriate \texttt{parse_small}
+% function in those cases.
+%
+% \begin{macro}[EXP]{\@@_parse_large:N}
+% This function is followed by the first non-zero digit of a
+% \enquote{large} significand ($\geq 1$). It is called within an
+% integer expression for the exponent. Grab up to $7$~more digits,
+% for a total of $8$~digits.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large:N #1
+ {
+ \exp_after:wN \@@_parse_large_leading:wwNN
+ \int_value:w 1 \token_to_str:N #1
+ \exp_after:wN \@@_parse_digits_vii:N
+ \exp:w \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_large_leading:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_large_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% We shift the exponent by the number of digits in~|#1|, namely the
+% target number, $8$, minus the \meta{number of zeros} (number of
+% digits missing). Then prepare to pack the $8$~first digits. If the
+% \meta{next token} is a digit, read up to $6$~more digits (digits
+% $10$ to~$15$). If it is a period, try to grab the end of our
+% $8$~first digits, branching to the \texttt{small} functions since
+% the number of digit does not affect the exponent anymore. Finally,
+% if this is the end of the significand, insert the \meta{zeros} to
+% complete the $8$~first digits, insert $8$~more, and look for an
+% exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_leading:wwNN 1 #1 ; #2; #3 #4
+ {
+ + \c_@@_half_prec_int - #3
+ \exp_after:wN \@@_parse_pack_leading:NNNNNww
+ \int_value:w \@@_int_eval:w 1 #1
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \@@_parse_large_trailing:wwNN
+ \int_value:w 1 \token_to_str:N #4
+ \exp_after:wN \@@_parse_digits_vi:N
+ \exp:w
+ \else:
+ \if:w . \exp_not:N #4
+ \exp_after:wN \@@_parse_small_leading:wwNN
+ \int_value:w 1
+ \cs:w
+ @@_parse_digits_
+ \@@_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN 0
+ \int_value:w 1 0000 0000
+ \@@_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_large_trailing:wwNN}
+% \begin{syntax}
+% \cs{@@_parse_large_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token}
+% \end{syntax}
+% We have just read $15$~digits. If the \meta{next token} is a digit,
+% then the exponent shift caused by this block of $8$~digits is~$8$,
+% first argument to the \texttt{pack_trailing} function. We keep the
+% \meta{digits} and this $16$-th digit, and find how this should be
+% rounded using \cs{@@_parse_large_round:NN}. Otherwise, the exponent
+% shift is the number of \meta{digits}, $7$~minus the \meta{number of
+% zeros}, and we test for a decimal point. This case happens in
+% |123451234512345.67| with exactly $15$ digits before the decimal
+% separator. Then branch to the appropriate \texttt{small} auxiliary,
+% grabbing a few more digits to complement the digits we already
+% grabbed. Finally, if this is truly the end of the significand, look
+% for an exponent after using the \meta{zeros} and providing a $16$-th
+% digit of~$0$.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #4 \exp_stop_f:
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \exp_after:wN \c_@@_half_prec_int
+ \int_value:w \@@_int_eval:w 1 #1 \token_to_str:N #4
+ \exp_after:wN \@@_parse_large_round:NN
+ \exp_after:wN #4
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
+ \int_value:w \@@_int_eval:w 7 - #3 \exp_stop_f:
+ \int_value:w \@@_int_eval:w 1 #1
+ \if:w . \exp_not:N #4
+ \exp_after:wN \@@_parse_small_trailing:wwNN
+ \int_value:w 1
+ \cs:w
+ @@_parse_digits_
+ \@@_int_to_roman:w #3
+ :N \exp_after:wN
+ \cs_end:
+ \exp:w
+ \else:
+ #2 0 \@@_parse_exponent:Nw #4
+ \fi:
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: beyond 16 digits, rounding}
+%
+% \begin{macro}[rEXP]{\@@_parse_round_loop:N, \@@_parse_round_up:N}
+% This loop is called when rounding a number (whether the mantissa is
+% small or large). It should appear in an integer expression. This
+% function reads digits one by one, until reaching a non-digit, and
+% adds~$1$ to the integer expression for each digit. If all digits
+% found are~$0$, the function ends the expression by |;0|,
+% otherwise by |;1|. This is done by switching the loop to
+% |round_up| at the first non-zero digit, thus we avoid to test
+% whether digits are~$0$ or not once we see a first non-zero digit.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_loop:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \if:w 0 \token_to_str:N #1
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_round_up:N
+ \exp:w
+ \fi:
+ \else:
+ \@@_parse_return_semicolon:w 0 #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_round_up:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ + 1
+ \exp_after:wN \@@_parse_round_up:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w 1 #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_round_after:wN}
+% After the loop \cs{@@_parse_round_loop:N}, this function fetches an
+% exponent with \cs{@@_parse_exponent:N}, and combines it with the
+% number of digits counted by \cs{@@_parse_round_loop:N}. At the same
+% time, the result |0| or |1| is added to the
+% surrounding integer expression.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_after:wN #1; #2
+ {
+ + #2 \exp_after:wN ;
+ \int_value:w \@@_int_eval:w #1 + \@@_parse_exponent:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_small_round:NN, \@@_parse_round_after:wN}
+% Here, |#1|~is the digit that we are currently rounding (we only care
+% whether it is even or odd). If |#2|~is not a digit, then fetch an
+% exponent and expand to |;|\meta{exponent} only. Otherwise, we
+% expand to |+0| or |+1|, then |;|\meta{exponent}. To
+% decide which, call \cs{@@_round_s:NNNw} to know whether to round up,
+% giving it as arguments a sign~$0$ (all explicit numbers are
+% positive), the digit |#1|~to round, the first following digit~|#2|,
+% and either |+0| or |+1| depending on whether the
+% following digits are all zero or not. This last argument is
+% obtained by \cs{@@_parse_round_loop:N}, whose number of digits we
+% discard by multiplying it by~$0$. The exponent which follows the
+% number is also fetched by \cs{@@_parse_round_after:wN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_value:w \@@_int_eval:w 0 * \@@_int_eval:w 0
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[rEXP]
+% {
+% \@@_parse_large_round:NN,
+% \@@_parse_large_round_test:NN,
+% \@@_parse_large_round_aux:wNN,
+% }
+% Large numbers are harder to round, as there may be a period in the
+% way. Again, |#1|~is the digit that we are currently rounding (we
+% only care whether it is even or odd). If there are no more digits
+% (|#2|~is not a digit), then we must test for a period: if there is
+% one, then switch to the rounding function for small significands,
+% otherwise fetch an exponent. If there are more digits (|#2|~is a
+% digit), then round, checking with \cs{@@_parse_round_loop:N} if all
+% further digits vanish, or some are non-zero. This loop is not
+% enough, as it is stopped by a period. After the loop, the
+% \texttt{aux} function tests for a period: if it is present, then we
+% must continue looking for digits, this time discarding the number of
+% digits we find.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_round:NN #1#2
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_value:w \@@_int_eval:w
+ \exp_after:wN \@@_parse_large_round_aux:wNN
+ \int_value:w \@@_int_eval:w 1
+ \exp_after:wN \@@_parse_round_loop:N
+ \else: %^^A could be dot, or e, or other
+ \exp_after:wN \@@_parse_large_round_test:NN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \@@_parse_large_round_test:NN #1#2
+ {
+ \if:w . \exp_not:N #2
+ \exp_after:wN \@@_parse_small_round:NN
+ \exp_after:wN #1
+ \exp:w
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_large_round_aux:wNN #1 ; #2 #3
+ {
+ + #2
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_value:w \@@_int_eval:w #1
+ \if:w . \exp_not:N #3
+ + 0 * \@@_int_eval:w 0
+ \exp_after:wN \@@_parse_round_loop:N
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN ;
+ \exp_after:wN 0
+ \exp_after:wN #3
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: finding the exponent}
+%
+% Expansion is a little bit tricky here, in part because we accept input
+% where multiplication is implicit.
+% \begin{syntax}
+% \cs{@@_parse:n} |{ 3.2 erf(0.1) }|
+% \cs{@@_parse:n} |{ 3.2 e\l_my_int }|
+% \cs{@@_parse:n} |{ 3.2 \c_pi_fp }|
+% \end{syntax}
+% The first case indicates that just looking one character ahead for an
+% \enquote{\texttt{e}} is not enough, since we would mistake the
+% function \texttt{erf} for an exponent of \enquote{\texttt{rf}}. An
+% alternative would be to look two tokens ahead and check if what
+% follows is a sign or a digit, considering in that case that we must be
+% finding an exponent. But taking care of the second case requires that
+% we unpack registers after \texttt{e}. However, blindly expanding the
+% two tokens ahead completely would break the third example (unpacking
+% is even worse). Indeed, in the course of reading $3.2$, \cs{c_pi_fp}
+% is expanded to \cs{s_@@} \cs{@@_chk:w} |1| |0| |{-1}| |{3141}|
+% $\cdots$ |;| and \cs{s_@@} stops the expansion. Expanding two tokens
+% ahead would then force the expansion of \cs{@@_chk:w} (despite it
+% being protected), and that function tries to produce an error.
+%
+% What can we do? Really, the reason why this last case breaks is that
+% just as \TeX{} does, we should read ahead as little as possible.
+% Here, the only case where there may be an exponent is if the first
+% token ahead is |e|. Then we expand (and possibly unpack) the second
+% token.
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent:Nw}
+% This auxiliary is convenient to smuggle some material through
+% \cs{fi:} ending conditional processing. We place those \cs{fi:}
+% (argument~|#2|) at a very odd place because this allows us to insert
+% \cs{@@_int_eval:w} \ldots{} there if needed.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent:Nw #1 #2 \@@_parse_expand:w
+ {
+ \exp_after:wN ;
+ \int_value:w #2 \@@_parse_exponent:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]
+% {\@@_parse_exponent:N, \@@_parse_exponent_aux:N}
+% This function should be called within an \cs{int_value:w}
+% expansion (or within an integer expression). It leaves digits of the
+% exponent behind it in the input stream, and terminates the expansion
+% with a semicolon. If there is no~|e|, leave an exponent of~$0$. If
+% there is an~|e|, expand the next token to run some tests on it. The
+% first rough test is that if the character code of~|#1| is greater
+% than that of~|9| (largest code valid for an exponent, less than any
+% code valid for an identifier), there was in fact no exponent;
+% otherwise, we search for the sign of the exponent.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent:N #1
+ {
+ \if:w e \exp_not:N #1
+ \exp_after:wN \@@_parse_exponent_aux:N
+ \exp:w
+ \else:
+ 0 \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_exponent_aux:N #1
+ {
+ \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #1
+ 0 \else: `#1 \fi: > `9 \exp_stop_f:
+ 0 \exp_after:wN ; \exp_after:wN e
+ \else:
+ \exp_after:wN \@@_parse_exponent_sign:N
+ \fi:
+ #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_sign:N}
+% Read signs one by one (if there is any).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_sign:N #1
+ {
+ \if:w + \if:w - \exp_not:N #1 + \fi: \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_sign:N
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN \@@_parse_exponent_body:N
+ \exp_after:wN #1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_body:N}
+% An exponent can be an explicit integer (most common case), or
+% various other things (most of which are invalid).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_body:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \@@_parse_exponent_keep:NTF #1
+ { \@@_parse_return_semicolon:w #1 }
+ {
+ \exp_after:wN ;
+ \exp:w
+ }
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_digits:N}
+% Read digits one by one, and leave them behind in the input stream.
+% When finding a non-digit, stop, and insert a semicolon. Note that
+% we do not check for overflow of the exponent, hence there can be a
+% \TeX{} error. It is mostly harmless, except when parsing
+% |0e9876543210|, which should be a valid representation of $0$, but
+% is not.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_exponent_digits:N #1
+ {
+ \if_int_compare:w 9 < 1 \token_to_str:N #1 \exp_stop_f:
+ \token_to_str:N #1
+ \exp_after:wN \@@_parse_exponent_digits:N
+ \exp:w
+ \else:
+ \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[rEXP]{\@@_parse_exponent_keep:NTF}
+% This is the last building block for parsing exponents. The
+% argument~|#1| is already fully expanded, and neither |+| nor~|-| nor
+% a digit. It can be:
+% \begin{itemize}
+% \item \cs{s_@@}, marking the start of an internal floating point,
+% invalid here;
+% \item another control sequence equal to \tn{relax}, probably a bad
+% variable;
+% \item a register: in this case we make sure that it is an integer
+% register, not a dimension;
+% \item a character other than |+|, |-| or digits, again, an error.
+% \end{itemize}
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_parse_exponent_keep:N #1 { TF }
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \if_meaning:w \scan_stop: #1
+ \if_int_compare:w
+ \@@_str_if_eq:nn { \s_@@ } { \exp_not:N #1 }
+ = 0 \exp_stop_f:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-after-e } { floating~point~ }
+ \prg_return_true:
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { bad-variable } {#1}
+ \prg_return_false:
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_str_if_eq:nn { \int_value:w #1 } { \tex_the:D #1 }
+ = 0 \exp_stop_f:
+ \int_value:w #1
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-after-e } { dimension~#1 }
+ \fi:
+ \prg_return_false:
+ \fi:
+ \else:
+ 0
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing } { exponent }
+ \prg_return_true:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Constants, functions and prefix operators}
+%
+% \subsubsection{Prefix operators}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_+:Nw}
+% A unary~|+| does nothing: we should continue looking for a number.
+% \begin{macrocode}
+\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_one:Nw
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_function:NNNwN}
+% Here, |#1| is a precedence, |#2| is some extra data used by some
+% functions, |#3| is \emph{e.g.}, \cs{@@_sin_o:w}, and expands once
+% after the calculation, |#4| is the operand, and |#5| is a
+% \cs[no-index]{@@_parse_infix_\ldots{}:N} function. We feed the data~|#2|, and the
+% argument~|#4|, to the function~|#3|, which expands
+% \cs{exp:w} thus the \texttt{infix} function~|#5|.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_function:NNNwN #1#2#3#4@#5
+ {
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_unary:NNNwN}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_chk:NwNw, \@@_parse_apply_unary_chk:nNNNw}
+% \begin{macro}[EXP]{\@@_parse_apply_unary_type:NNN, \@@_parse_apply_unary_error:NNw}
+% In contrast to \cs{@@_parse_apply_function:NNNwN}, this checks that
+% the operand |#4| is a single argument (namely there is a single
+% |;|). We use the fact that any floating point starts with a
+% \enquote{safe} token like \cs{s_@@}. If there is no argument
+% produce the |fp-no-arg| error; if there are at least two produce
+% |fp-multi-arg|. For the error message extract the mathematical
+% function name (such as |sin|) from the \pkg{expl3} function that
+% computes it, such as \cs{@@_sin_o:w}.
+%
+% In addition, since there is a single argument we can dispatch on
+% type and check that the resulting function exists. This catches
+% things like |sin((1,2))| where it does not make sense to take the
+% sine of a tuple.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5
+ {
+ \@@_parse_apply_unary_chk:NwNw #4 @ ; . \q_stop
+ \@@_parse_apply_unary_type:NNN
+ #3 #2 #4 @
+ \exp:w \exp_end_continue_f:w #5 #1
+ }
+\cs_new:Npn \@@_parse_apply_unary_chk:NwNw #1#2 ; #3#4 \q_stop
+ {
+ \if_meaning:w @ #3 \else:
+ \token_if_eq_meaning:NNTF . #3
+ { \@@_parse_apply_unary_chk:nNNNNw { no } }
+ { \@@_parse_apply_unary_chk:nNNNNw { multi } }
+ \fi:
+ }
+\cs_new:Npn \@@_parse_apply_unary_chk:nNNNNw #1#2#3#4#5#6 @
+ {
+ #2
+ \@@_error:nffn { fp-#1-arg } { \@@_func_to_name:N #4 } { } { }
+ \exp_after:wN #4 \exp_after:wN #5 \c_nan_fp @
+ }
+\cs_new:Npn \@@_parse_apply_unary_type:NNN #1#2#3
+ {
+ \@@_change_func_type:NNN #3 #1 \@@_parse_apply_unary_error:NNw
+ #2 #3
+ }
+\cs_new:Npn \@@_parse_apply_unary_error:NNw #1#2#3 @
+ { \@@_invalid_operation_o:fw { \@@_func_to_name:N #1 } #3 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
+% The unary~|-| and boolean not are harder: we parse the operand using
+% a precedence equal to the maximum of the previous precedence~|##1|
+% and the precedence \cs{c_@@_prec_not_int} of the unary operator, then call
+% the appropriate |\__fp_|\meta{operation}|_o:w| function,
+% where the \meta{operation} is |set_sign| or |not|.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:cpn { @@_parse_prefix_ #1 :Nw } ##1
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN ##1
+ \exp_after:wN #4
+ \exp_after:wN #3
+ \exp:w
+ \if_int_compare:w #2 < ##1
+ \@@_parse_operand:Nw ##1
+ \else:
+ \@@_parse_operand:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+ }
+\@@_tmp:w - \c_@@_prec_not_int \@@_set_sign_o:w 2
+\@@_tmp:w ! \c_@@_prec_not_int \@@_not_o:w ?
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_.:Nw}
+% Numbers which start with a decimal separator (a~period) end up here.
+% Of course, we do not look for an operand, but for the rest of the
+% number. This function is very similar to \cs{@@_parse_one_digit:NN}
+% but calls \cs{@@_parse_strim_zeros:N} to trim zeros after the
+% decimal point, rather than the \texttt{trim_zeros} function for
+% zeros before the decimal point.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_.:Nw } #1
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_sanitize:wN
+ \int_value:w \@@_int_eval:w 0 \@@_parse_strim_zeros:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_parse_prefix_(:Nw, \@@_parse_lparen_after:NwN}
+% The left parenthesis is treated as a unary prefix operator because
+% it appears in exactly the same settings. If the previous precedence
+% is \cs{c_@@_prec_func_int} we are parsing arguments of a function
+% and commas should not build tuples; otherwise commas should build
+% tuples. We distinguish these cases by precedence:
+% \cs{c_@@_prec_comma_int} for the case of arguments,
+% \cs{c_@@_prec_tuple_int} for the case of tuples.
+% Once the operand is found, the \texttt{lparen_after} auxiliary makes
+% sure that there was a closing parenthesis (otherwise it complains),
+% and leaves in the input stream an operand,
+% fetching the following infix operator.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_(:Nw } #1
+ {
+ \exp_after:wN \@@_parse_lparen_after:NwN
+ \exp_after:wN #1
+ \exp:w
+ \if_int_compare:w #1 = \c_@@_prec_func_int
+ \@@_parse_operand:Nw \c_@@_prec_comma_int
+ \else:
+ \@@_parse_operand:Nw \c_@@_prec_tuple_int
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npx \@@_parse_lparen_after:NwN #1#2 @ #3
+ {
+ \exp_not:N \token_if_eq_meaning:NNTF #3
+ \exp_not:c { @@_parse_infix_):N }
+ {
+ \exp_not:N \@@_exp_after_array_f:w #2 \s_@@_stop
+ \exp_not:N \exp_after:wN
+ \exp_not:N \@@_parse_infix_after_paren:NN
+ \exp_not:N \exp_after:wN #1
+ \exp_not:N \exp:w
+ \exp_not:N \@@_parse_expand:w
+ }
+ {
+ \exp_not:N \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing } { ) }
+ \exp_not:N \tl_if_empty:nT {#2} \exp_not:N \c_@@_empty_tuple_fp
+ #2 @
+ \exp_not:N \use_none:n #3
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_prefix_):Nw}
+% The right parenthesis can appear as a prefix in two similar cases:
+% in an empty tuple or tuple ending with a comma, or in an empty
+% argument list or argument list ending with a comma, such as in
+% |max(1,2,)| or in |rand()|.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_prefix_):Nw } #1
+ {
+ \if_int_compare:w #1 = \c_@@_prec_comma_int
+ \else:
+ \if_int_compare:w #1 = \c_@@_prec_tuple_int
+ \exp_after:wN \c_@@_empty_tuple_fp \exp:w
+ \else:
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing-number } { ) }
+ \exp_after:wN \c_nan_fp \exp:w
+ \fi:
+ \exp_end_continue_f:w
+ \fi:
+ \@@_parse_infix_after_paren:NN #1 )
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Constants}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_inf:N , \@@_parse_word_nan:N ,
+% \@@_parse_word_pi:N , \@@_parse_word_deg:N ,
+% \@@_parse_word_true:N , \@@_parse_word_false:N ,
+% }
+% Some words correspond to constant floating points. The floating
+% point constant is left as a result of \cs{@@_parse_one:Nw} after
+% expanding \cs{@@_parse_infix:NN}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ { \exp_after:wN #2 \exp:w \exp_end_continue_f:w \@@_parse_infix:NN }
+ }
+\@@_tmp:w { inf } \c_inf_fp
+\@@_tmp:w { nan } \c_nan_fp
+\@@_tmp:w { pi } \c_pi_fp
+\@@_tmp:w { deg } \c_one_degree_fp
+\@@_tmp:w { true } \c_one_fp
+\@@_tmp:w { false } \c_zero_fp
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_caseless_inf:N,
+% \@@_parse_caseless_infinity:N,
+% \@@_parse_caseless_nan:N
+% }
+% Copies of \cs[no-index]{@@_parse_word_\ldots{}:N} commands, to allow
+% arbitrary case as mandated by the standard.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_parse_caseless_inf:N \@@_parse_word_inf:N
+\cs_new_eq:NN \@@_parse_caseless_infinity:N \@@_parse_word_inf:N
+\cs_new_eq:NN \@@_parse_caseless_nan:N \@@_parse_word_nan:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_word_pt:N , \@@_parse_word_in:N ,
+% \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N ,
+% \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N ,
+% \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N ,
+% }
+% Dimension units are also floating point constants but their value is
+% not stored as a floating point constant. We give the values
+% explicitly here.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1 #2
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ {
+ \@@_exp_after_f:nw { \@@_parse_infix:NN }
+ \s_@@ \@@_chk:w 10 #2 ;
+ }
+ }
+\@@_tmp:w {pt} { {1} {1000} {0000} {0000} {0000} }
+\@@_tmp:w {in} { {2} {7227} {0000} {0000} {0000} }
+\@@_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} }
+\@@_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} }
+\@@_tmp:w {mm} { {1} {2845} {2755} {9055} {1181} }
+\@@_tmp:w {dd} { {1} {1070} {0085} {6496} {0630} }
+\@@_tmp:w {cc} { {2} {1284} {0102} {7795} {2756} }
+\@@_tmp:w {nd} { {1} {1066} {9783} {4645} {6693} }
+\@@_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} }
+\@@_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} }
+\@@_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_word_em:N, \@@_parse_word_ex:N}
+% The font-dependent units |em| and |ex| must be evaluated on the fly.
+% We reuse an auxiliary of \cs{dim_to_fp:n}.
+% \begin{macrocode}
+\tl_map_inline:nn { {em} {ex} }
+ {
+ \cs_new:cpn { @@_parse_word_#1:N }
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \int_value:w \dim_to_decimal_in_sp:n { 1 #1 } \exp_after:wN ;
+ \exp:w \exp_end_continue_f:w \@@_parse_infix:NN
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Functions}
+%
+% ^^A begin[todo]
+%
+% \begin{macro}[EXP]
+% {\@@_parse_unary_function:NNN, \@@_parse_function:NNN}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_unary_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_function:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_func_int \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Main functions}
+%
+% \begin{macro}[EXP]{\@@_parse:n, \@@_parse_o:n}
+% \begin{macro}[EXP]{\@@_parse_after:ww}
+% Start an \cs{exp:w} expansion so that \cs{@@_parse:n} expands
+% in two steps. The \cs{@@_parse_operand:Nw} function performs
+% computations until reaching an operation with precedence
+% \cs{c_@@_prec_end_int} or less, namely, the end of the expression. The
+% marker \cs{s_@@_mark} indicates that the next token is an already
+% parsed version of an infix operator, and \cs{@@_parse_infix_end:N}
+% has infinitely negative precedence. Finally, clean up a
+% (well-defined) set of extra tokens and stop the initial expansion
+% with \cs{exp_end:}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse:n #1
+ {
+ \exp:w
+ \exp_after:wN \@@_parse_after:ww
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_end_int
+ \@@_parse_expand:w #1
+ \s_@@_mark \@@_parse_infix_end:N
+ \s_@@_stop
+ \exp_end:
+ }
+\cs_new:Npn \@@_parse_after:ww
+ #1@ \@@_parse_infix_end:N \s_@@_stop #2 { #2 #1 }
+\cs_new:Npn \@@_parse_o:n #1
+ {
+ \exp:w
+ \exp_after:wN \@@_parse_after:ww
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_end_int
+ \@@_parse_expand:w #1
+ \s_@@_mark \@@_parse_infix_end:N
+ \s_@@_stop
+ {
+ \exp_end_continue_f:w
+ \@@_exp_after_any_f:nw { \exp_after:wN \exp_stop_f: }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_operand:Nw}
+% \begin{macro}[EXP]{\@@_parse_continue:NwN}
+% This is just a shorthand which sets up both \cs{@@_parse_continue:NwN}
+% and \cs{@@_parse_one:Nw} with the same precedence. Note the
+% trailing \cs{exp:w}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_operand:Nw #1
+ {
+ \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_one:Nw
+ \exp_after:wN #1
+ \exp:w
+ }
+\cs_new:Npn \@@_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_apply_binary:NwNwN}
+% \begin{macro}[EXP]
+% {\@@_parse_apply_binary_chk:NN, \@@_parse_apply_binary_error:NNN}
+% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation}
+% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate
+% call to the \meta{operation}~|#3|, dispatching on both types.
+% If the resulting control sequence does not exist, the operation is
+% not allowed.
+%
+% This is redefined in \pkg{l3fp-extras}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2#3@ #4 #5#6@ #7
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #2
+ _#4
+ \@@_type_from_scan:N #5
+ _o:ww
+ \cs_end:
+ #4
+ #2#3 #5#6
+ \exp:w \exp_end_continue_f:w #7 #1
+ }
+\cs_new:Npn \@@_parse_apply_binary_chk:NN #1#2
+ {
+ \if_meaning:w \scan_stop: #1
+ \@@_parse_apply_binary_error:NNN #2
+ \fi:
+ #1
+ }
+\cs_new:Npn \@@_parse_apply_binary_error:NNN #1#2#3
+ {
+ #2
+ \@@_invalid_operation_o:Nww #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_binary_type_o:Nww, \@@_binary_rev_type_o:Nww}
+% Applies the operator |#1| to its two arguments, dispatching
+% according to their types, and expands once after the result.
+% The |rev| version swaps its arguments before doing this.
+% \begin{macrocode}
+\cs_new:Npn \@@_binary_type_o:Nww #1 #2#3 ; #4
+ {
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #2
+ _ #1
+ \@@_type_from_scan:N #4
+ _o:ww
+ \cs_end:
+ #1
+ #2 #3 ; #4
+ }
+\cs_new:Npn \@@_binary_rev_type_o:Nww #1 #2#3 ; #4#5 ;
+ {
+ \exp_after:wN \@@_parse_apply_binary_chk:NN
+ \cs:w
+ @@
+ \@@_type_from_scan:N #4
+ _ #1
+ \@@_type_from_scan:N #2
+ _o:ww
+ \cs_end:
+ #1
+ #4 #5 ; #2 #3 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Infix operators}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_after_operand:NwN}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_after_operand:NwN #1 #2;
+ {
+ \@@_exp_after_f:nw { \@@_parse_infix:NN #1 }
+ #2;
+ }
+\cs_new:Npn \@@_parse_infix:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if_int_compare:w
+ \@@_str_if_eq:nn { \s_@@_mark } { \exp_not:N #2 }
+ = 0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_juxt:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_juxt:N
+ \else:
+ \exp_after:wN \@@_parse_infix_check:NNN
+ \cs:w
+ @@_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+\cs_new:Npn \@@_parse_infix_check:NNN #1#2#3
+ {
+ \if_meaning:w \scan_stop: #1
+ \__kernel_msg_expandable_error:nnn
+ { kernel } { fp-missing } { * }
+ \exp_after:wN \@@_parse_infix_mul:N
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \else:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_parse_infix_after_paren:NN}
+% Variant of \cs{@@_parse_infix:NN} for use after a closing
+% parenthesis. The only difference is that \cs{@@_parse_infix_juxt:N}
+% is replaced by \cs{@@_parse_infix_mul:N}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_after_paren:NN #1 #2
+ {
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if_int_compare:w
+ \@@_str_if_eq:nn { \s_@@_mark } { \exp_not:N #2 }
+ = 0 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mark:NNN
+ \else:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mul:N
+ \fi:
+ \else:
+ \if_int_compare:w
+ \@@_int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - 32 \fi: ) / 26
+ = 3 \exp_stop_f:
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_mul:N
+ \else:
+ \exp_after:wN \@@_parse_infix_check:NNN
+ \cs:w
+ @@_parse_infix_ \token_to_str:N #2 :N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
+ \fi:
+ #1
+ #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Closing parentheses and commas}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_mark:NNN}
+% As an infix operator, \cs{s_@@_mark} means that the next
+% token~(|#3|) has already gone through \cs{@@_parse_infix:NN} and
+% should be provided the precedence~|#1|. The scan mark~|#2| is
+% discarded.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_mark:NNN #1#2#3 { #3 #1 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_end:N}
+% This one is a little bit odd: force every previous operator to end,
+% regardless of the precedence.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_end:N #1
+ { @ \use_none:n \@@_parse_infix_end:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_):N+
+% This is very similar to \cs{@@_parse_infix_end:N}, complaining about
+% an extra closing parenthesis if the previous operator was the
+% beginning of the expression, with precedence \cs{c_@@_prec_end_int}.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c_@@_prec_end_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-extra } { ) }
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_):N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[verb, EXP]{\__fp_parse_infix_,:N}
+% \begin{macro}[EXP]{\@@_parse_infix_comma:w, \@@_parse_apply_comma:NwNwN}
+% As for other infix operations, if the previous operations has higher
+% precedence the comma waits. Otherwise we call
+% \cs{@@_parse_operand:Nw} to read more comma-delimited arguments that
+% \cs{@@_parse_infix_comma:w} simply concatenates into a |@|-delimited
+% array. The first comma in a tuple that is not a function argument
+% is distinguished: in that case call \cs{@@_parse_apply_comma:NwNwN}
+% whose job is to convert the first item of the tuple and an array of
+% the remaining items into a tuple. In contrast to
+% \cs{@@_parse_apply_binary:NwNwN} this function's operands are not
+% single-object arrays.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 > \c_@@_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \else:
+ \if_int_compare:w ##1 < \c_@@_prec_comma_int
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_comma:NwNwN
+ \exp_after:wN ,
+ \exp:w
+ \else:
+ \exp_after:wN \@@_parse_infix_comma:w
+ \exp:w
+ \fi:
+ \@@_parse_operand:Nw \c_@@_prec_comma_int
+ \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_,:N }
+\cs_new:Npn \@@_parse_infix_comma:w #1 @
+ { #1 @ \use_none:n }
+\cs_new:Npn \@@_parse_apply_comma:NwNwN #1 #2@ #3 #4@ #5
+ {
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_tuple_f:nw { }
+ \s_@@_tuple \@@_tuple_chk:w { #2 #4 } ;
+ #5 #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Usual infix operators}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_infix_+:N, \@@_parse_infix_-:N,
+% \@@_parse_infix_juxt:N,
+% \@@_parse_infix_/:N, \@@_parse_infix_mul:N,
+% \@@_parse_infix_and:N, \@@_parse_infix_or:N,
+% }
+% \begin{macro}[EXP]+\@@_parse_infix_^:N+
+% As described in the \enquote{work plan}, each infix operator has an
+% associated |\..._infix_...| function, a computing function, and
+% precedence, given as arguments to \cs{@@_tmp:w}. Using the general
+% mechanism for arithmetic operations. The power operation must be
+% associative in the opposite order from all others. For this, we use
+% two distinct precedences.
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < #3
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_binary:NwNwN
+ \exp_after:wN #2
+ \exp:w
+ \@@_parse_operand:Nw #4
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_^:N } ^
+ \c_@@_prec_hatii_int \c_@@_prec_hat_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_juxt:N } *
+ \c_@@_prec_juxt_int \c_@@_prec_juxt_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_/:N } /
+ \c_@@_prec_times_int \c_@@_prec_times_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_mul:N } *
+ \c_@@_prec_times_int \c_@@_prec_times_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_-:N } -
+ \c_@@_prec_plus_int \c_@@_prec_plus_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_+:N } +
+ \c_@@_prec_plus_int \c_@@_prec_plus_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_and:N } &
+ \c_@@_prec_and_int \c_@@_prec_and_int
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_or:N } |
+ \c_@@_prec_or_int \c_@@_prec_or_int
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Juxtaposition}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_(:N+
+% When an opening parenthesis appears where we expect an infix
+% operator, we compute the product of the previous operand and the
+% contents of the parentheses using \cs{@@_parse_infix_mul:N}.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_(:N } #1
+ { \@@_parse_infix_mul:N #1 ( }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Multi-character cases}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_*:N}
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1
+ {
+ \cs_new:cpn { @@_parse_infix_*:N } ##1##2
+ {
+ \if:w * \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \else:
+ \exp_after:wN \@@_parse_infix_mul:N
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_^:N }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]+\@@_parse_infix_|:Nw+
+% \begin{macro}[EXP]+\@@_parse_infix_&:Nw+
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \cs_new:Npn #1 ##1##2
+ {
+ \if:w #2 \exp_not:N ##2
+ \exp_after:wN #1
+ \exp_after:wN ##1
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN #3
+ \exp_after:wN ##1
+ \exp_after:wN ##2
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_|:N } | \@@_parse_infix_or:N
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_&:N } & \@@_parse_infix_and:N
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Ternary operator}
+%
+% \begin{macro}[EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N}
+% \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < \c_@@_prec_quest_int
+ #4
+ \exp_after:wN @
+ \exp_after:wN #2
+ \exp:w
+ \@@_parse_operand:Nw #3
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_?:N }
+ \@@_ternary:NwwN \c_@@_prec_quest_int { }
+\exp_args:Nc \@@_tmp:w { @@_parse_infix_::N }
+ \@@_ternary_auxii:NwwN \c_@@_prec_colon_int
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { fp-missing } { ? } { ~for~?: }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Comparisons}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_parse_infix_<:N, \@@_parse_infix_=:N,
+% \@@_parse_infix_>:N, \@@_parse_infix_!:N
+% }
+% \begin{macro}[EXP]
+% {
+% \@@_parse_excl_error:,
+% \@@_parse_compare:NNNNNNN,
+% \@@_parse_compare_auxi:NNNNNNN,
+% \@@_parse_compare_auxii:NNNNN,
+% \@@_parse_compare_end:NNNNw,
+% \@@_compare:wNNNNw,
+% }
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_<:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 < }
+\cs_new:cpn { @@_parse_infix_=:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 = }
+\cs_new:cpn { @@_parse_infix_>:N } #1
+ { \@@_parse_compare:NNNNNNN #1 1 0 0 0 0 > }
+\cs_new:cpn { @@_parse_infix_!:N } #1
+ {
+ \exp_after:wN \@@_parse_compare:NNNNNNN
+ \exp_after:wN #1
+ \exp_after:wN 0
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ \exp_after:wN 1
+ }
+\cs_new:Npn \@@_parse_excl_error:
+ {
+ \__kernel_msg_expandable_error:nnnn
+ { kernel } { fp-missing } { = } { ~after~!. }
+ }
+\cs_new:Npn \@@_parse_compare:NNNNNNN #1
+ {
+ \if_int_compare:w #1 < \c_@@_prec_comp_int
+ \exp_after:wN \@@_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \@@_parse_excl_error:
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_compare:NNNNNNN
+ \fi:
+ }
+\cs_new:Npn \@@_parse_compare_auxi:NNNNNNN #1#2#3#4#5#6#7
+ {
+ \if_case:w
+ \@@_int_eval:w \exp_after:wN ` \token_to_str:N #7 - `<
+ \@@_int_eval_end:
+ \@@_parse_compare_auxii:NNNNN #2#2#4#5#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#2#5#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#4#2#6
+ \or: \@@_parse_compare_auxii:NNNNN #2#3#4#5#2
+ \else: #1 \@@_parse_compare_end:NNNNw #3#4#5#6#7
+ \fi:
+ }
+\cs_new:Npn \@@_parse_compare_auxii:NNNNN #1#2#3#4#5
+ {
+ \exp_after:wN \@@_parse_compare_auxi:NNNNNNN
+ \exp_after:wN \prg_do_nothing:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp_after:wN #5
+ \exp:w \exp_after:wN \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_compare_end:NNNNw #1#2#3#4#5 \fi:
+ {
+ \fi:
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_compare:NwNNNNNwN
+ \exp_after:wN \c_one_fp
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp_after:wN #4
+ \exp:w
+ \@@_parse_operand:Nw \c_@@_prec_comp_int \@@_parse_expand:w #5
+ }
+\cs_new:Npn \@@_parse_apply_compare:NwNNNNNwN
+ #1 #2@ #3 #4#5#6#7 #8@ #9
+ {
+ \if_int_odd:w
+ \if_meaning:w \c_zero_fp #3
+ 0
+ \else:
+ \if_case:w \@@_compare_back_any:ww #8 #2 \exp_stop_f:
+ #5 \or: #6 \or: #7 \else: #4
+ \fi:
+ \fi:
+ \exp_stop_f:
+ \exp_after:wN \@@_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_one_fp
+ \else:
+ \exp_after:wN \@@_parse_apply_compare_aux:NNwN
+ \exp_after:wN \c_zero_fp
+ \fi:
+ #1 #8 #9
+ }
+\cs_new:Npn \@@_parse_apply_compare_aux:NNwN #1 #2 #3; #4
+ {
+ \if_meaning:w \@@_parse_compare:NNNNNNN #4
+ \exp_after:wN \@@_parse_continue_compare:NNwNN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \@@_exp_after_o:w #3;
+ \exp:w \exp_end_continue_f:w
+ \else:
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #2
+ \exp:w \exp_end_continue_f:w
+ \exp_after:wN #1
+ \exp:w \exp_end_continue_f:w
+ \fi:
+ #4 #2
+ }
+\cs_new:Npn \@@_parse_continue_compare:NNwNN #1#2 #3@ #4#5
+ { #4 #2 #3@ #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Tools for functions}
+%
+% \begin{macro}[EXP]{\@@_parse_function_all_fp_o:fnw}
+% Followed by \Arg{function name} \Arg{code} \meta{float array} |@|
+% this checks all floats are floating point numbers (no tuples).
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_function_all_fp_o:fnw #1#2#3 @
+ {
+ \@@_array_if_all_fp:nTF {#3}
+ { #2 #3 @ }
+ {
+ \@@_error:nffn { fp-bad-args }
+ {#1}
+ { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#3} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_parse_function_one_two:nnw}
+% \begin{macro}[EXP]
+% {
+% \@@_parse_function_one_two_error_o:w,
+% \@@_parse_function_one_two_aux:nnw,
+% \@@_parse_function_one_two_auxii:nnw
+% }
+% This is followed by \Arg{function name} \Arg{code} \meta{float
+% array} |@|. It checks that the \meta{float array} consists of one
+% or two floating point numbers (not tuples), then leaves the
+% \meta{code} (if there is one float) or its tail (if there are two
+% floats) followed by the \meta{float array}. The \meta{code} should
+% start with a single token such as \cs{@@_atan_default:w} that deals
+% with the single-float case.
+%
+% The first \cs{@@_if_type_fp:NTwFw} test catches the case of no
+% argument and the case of a tuple argument. The next one
+% distinguishes the case of a single argument (no error, just add
+% \cs{c_one_fp}) from a tuple second argument. Finally check there is
+% no further argument.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_function_one_two:nnw #1#2#3
+ {
+ \@@_if_type_fp:NTwFw
+ #3 { } \s_@@ \@@_parse_function_one_two_error_o:w \q_stop
+ \@@_parse_function_one_two_aux:nnw {#1} {#2} #3
+ }
+\cs_new:Npn \@@_parse_function_one_two_error_o:w #1#2#3#4 @
+ {
+ \@@_error:nffn { fp-bad-args }
+ {#2}
+ { \fp_to_tl:n { \s_@@_tuple \@@_tuple_chk:w {#4} ; } }
+ { }
+ \exp_after:wN \c_nan_fp
+ }
+\cs_new:Npn \@@_parse_function_one_two_aux:nnw #1#2 #3; #4
+ {
+ \@@_if_type_fp:NTwFw
+ #4 { }
+ \s_@@
+ {
+ \if_meaning:w @ #4
+ \exp_after:wN \use_iv:nnnn
+ \fi:
+ \@@_parse_function_one_two_error_o:w
+ }
+ \q_stop
+ \@@_parse_function_one_two_auxii:nnw {#1} {#2} #3; #4
+ }
+\cs_new:Npn \@@_parse_function_one_two_auxii:nnw #1#2#3; #4; #5
+ {
+ \if_meaning:w @ #5 \else:
+ \exp_after:wN \@@_parse_function_one_two_error_o:w
+ \fi:
+ \use_ii:nn {#1} { \use_none:n #2 } #3; #4; #5
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_map_o:nw, \@@_tuple_map_loop_o:nw}
+% Apply |#1| to all items in the following tuple and expand once
+% afterwards. The code |#1| should itself expand once after its
+% result.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_map_o:nw #1 \s_@@_tuple \@@_tuple_chk:w #2 ;
+ {
+ \exp_after:wN \s_@@_tuple
+ \exp_after:wN \@@_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_map_loop_o:nw {#1} #2
+ { \s_@@ \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \@@_tuple_map_loop_o:nw #1#2#3 ;
+ {
+ \use_none:n #2
+ #1 #2 #3 ;
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_map_loop_o:nw {#1}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_tuple_mapthread_o:nww, \@@_tuple_mapthread_loop_o:nw}
+% Apply |#1| to pairs of items in the two following tuples and expand once
+% afterwards.
+% \begin{macrocode}
+\cs_new:Npn \@@_tuple_mapthread_o:nww #1
+ \s_@@_tuple \@@_tuple_chk:w #2 ;
+ \s_@@_tuple \@@_tuple_chk:w #3 ;
+ {
+ \exp_after:wN \s_@@_tuple
+ \exp_after:wN \@@_tuple_chk:w
+ \exp_after:wN {
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_mapthread_loop_o:nw {#1}
+ #2 { \s_@@ \prg_break: } ; @
+ #3 { \s_@@ \prg_break: } ;
+ \prg_break_point:
+ \exp_after:wN } \exp_after:wN ;
+ }
+\cs_new:Npn \@@_tuple_mapthread_loop_o:nw #1#2#3 ; #4 @ #5#6 ;
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 #2 #3 ; #5 #6 ;
+ \exp:w \exp_end_continue_f:w
+ \@@_tuple_mapthread_loop_o:nw {#1} #4 @
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% ^^A end[todo]
+%
+% \subsection{Messages}
+%
+% \begin{macrocode}
+\__kernel_msg_new:nnn { kernel } { fp-deprecated }
+ { '#1'~deprecated;~use~'#2' }
+\__kernel_msg_new:nnn { kernel } { unknown-fp-word }
+ { Unknown~fp~word~#1. }
+\__kernel_msg_new:nnn { kernel } { fp-missing }
+ { Missing~#1~inserted #2. }
+\__kernel_msg_new:nnn { kernel } { fp-extra }
+ { Extra~#1~ignored. }
+\__kernel_msg_new:nnn { kernel } { fp-early-end }
+ { Premature~end~in~fp~expression. }
+\__kernel_msg_new:nnn { kernel } { fp-after-e }
+ { Cannot~use~#1 after~'e'. }
+\__kernel_msg_new:nnn { kernel } { fp-missing-number }
+ { Missing~number~before~'#1'. }
+\__kernel_msg_new:nnn { kernel } { fp-unknown-symbol }
+ { Unknown~symbol~#1~ignored. }
+\__kernel_msg_new:nnn { kernel } { fp-extra-comma }
+ { Unexpected~comma~turned~to~nan~result. }
+\__kernel_msg_new:nnn { kernel } { fp-no-arg }
+ { #1~got~no~argument;~used~nan. }
+\__kernel_msg_new:nnn { kernel } { fp-multi-arg }
+ { #1~got~more~than~one~argument;~used~nan. }
+\__kernel_msg_new:nnn { kernel } { fp-num-args }
+ { #1~expects~between~#2~and~#3~arguments. }
+\__kernel_msg_new:nnn { kernel } { fp-bad-args }
+ { Arguments~in~#1#2~are~invalid. }
+\__kernel_msg_new:nnn { kernel } { fp-infty-pi }
+ { Math~command~#1 is~not~an~fp }
+%<*package>
+\cs_if_exist:cT { @unexpandable@protect }
+ {
+ \__kernel_msg_new:nnn { kernel } { fp-robust-cmd }
+ { Robust~command~#1 invalid~in~fp~expression! }
+ }
+%</package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex