% \iffalse meta-comment
%
%% File: l3fp-parse.dtx Copyright (C) 2011-2014 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
%%
%%    http://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 released version of this bundle is available from CTAN.
%%
%% -----------------------------------------------------------------------
%%
%% The development version of the bundle can be found at
%%
%%    http://www.latex-project.org/svnroot/experimental/trunk/
%%
%% for those people who are interested.
%%
%%%%%%%%%%%
%% NOTE: %%
%%%%%%%%%%%
%%
%%   Snapshots taken from the repository represent work in progress and may
%%   not work or may contain conflicting material!  We therefore ask
%%   people _not_ to put them into distributions, archives, etc. without
%%   prior consultation with the LaTeX Project Team.
%%
%% -----------------------------------------------------------------------
%%
%
%<*driver>
\documentclass[full]{l3doc}
\GetIdInfo$Id: l3fp-parse.dtx 5383 2014-08-25 13:17:55Z bruno $
  {L3 Floating-point expression parsing}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{The \textsf{l3fp-parse} package\thanks{This file
%         has version number \fileversion, last
%         revised \filedate.}\\
% 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 \filedate}
%
% \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.
%
% \begin{macro}[EXP, int]{\@@_parse:n}
%   \begin{syntax}
%     \cs{@@_parse:n} \Arg{fpexpr}
%   \end{syntax}
%   Evaluates the \meta{floating point expression} and leaves the result
%   in the input stream as an internal floating point number.  This
%   function forms the basis of almost all public \pkg{l3fp} functions.
%   During evaluation, each token is fully \texttt{f}-expanded.
%   \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 will lead to
%     unrecoverable low-level \TeX{} errors.
%   \end{texnote}
% \end{macro}
%
% Floating point expressions are composed of numbers, given in various
% forms, infix operators, such as |+|, |**|, or~|,| (which joins two
% numbers into a list), and prefix operators, such as the unary~|-|,
% functions, or opening parentheses.  Here is a list of precedences
% which control the order of evaluation (some distinctions are
% irrelevant for the order of evaluation, but serve as signals), from
% the tightest binding to the loosest binding.
% \begin{itemize}
% \item[16] Function calls with multiple arguments.
% \item[15] Function calls expecting exactly one argument.
% \item[14] Binary |**| and~|^| (right to left).
% \item[12] Unary |+|, |-|, |!| (right to left).
% \item[10] Binary |*|, |/|, and juxtaposition (implicit~|*|).
% \item[9] Binary |+| and~|-|.
% \item[7] Comparisons.
% \item[5] Logical \texttt{and}, denoted by~|&&|.
% \item[4] Logical \texttt{or}, denoted by~\verb*+||+.
% \item[3] Ternary operator |?:|, piece~|?|.
% \item[2] Ternary operator |?:|, piece~|:|.
% \item[1] Commas, and parentheses accepting commas.
% \item[0] Parentheses expecting exactly one argument.
% \item[-1] Start and end of the expression.
% \end{itemize}
%
% \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
% will grow 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 much 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{quote}\ttfamily
%   \cs{exp_after:wN} \cs{add:ww}
%   \cs{__int_value:w} 12345 \cs{exp_after:wN} ; \newline
%   \cs{tex_romannumeral:D} |\operand:w| \meta{stuff}
% \end{quote}
% 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{tex_romannumeral:D}, which expands |\operand:w|, defined to read
% what follows and make a number out of it, then leave \cs{c_zero}, the
% number, and a semicolon in the input stream.  Once |\operand:w| is
% done expanding, we obtain essentially
% \begin{quote}\ttfamily
%   \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; \newline
%   \cs{tex_romannumeral:D} \cs{c_zero} 333444 ;
% \end{quote}
% where in fact \cs{exp_after:wN} has already been expanded,
% \cs{__int_value:w} has already seen |12345|, and
% \cs{tex_romannumeral:D} is still looking for a number.  It finds
% \cs{c_zero}, 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{quote}\ttfamily
%   \cs{add:ww} 12345 ; 333444 ;
% \end{quote}
% 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 \cs{add:ww}.  Just like \cs{__int_value:w} |12345|
% \cs{exp_after:wN}~|;| expanded what follows once, we need \cs{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{@@_..._o:ww} expand what follows once.  This comes at the
% cost of leaving tokens in the input stack, and we will 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 the calculation
% |41-2^3*4+5| will be done.  Here, we abuse notations: the first
% argument of |\operand:Nw| should be an integer constant (\cs{c_three},
% \cs{c_nine}, \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 will be 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} \newline
%   ~~|\__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{quote}
%   |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
% \end{quote}
% 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{quote}
%   |@| \cs{@@_parse_apply_binary:NwNwN} \newline
%   ~~~~\meta{operator} \meta{number_2} \newline
%   |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
% \end{quote}
% 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{quote}
%   \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline
%   \cs{exp_after:wN} \meta{precedence} \newline
%   \cs{tex_romannumeral:D} |-`0| \newline
%   ~~\cs{@@_parse_one:Nw} \meta{precedence}
% \end{quote}
% 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{quote}
%   \cs{@@_parse_continue:NwN} \meta{precedence} \newline
%   \meta{number} |@| \newline
%   \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
% \end{quote}
% or
% \begin{quote}
%   \cs{@@_parse_continue:NwN} \meta{precedence} \newline
%   \meta{number} |@| \newline
%   \cs{@@_parse_apply_binary:NwNwN} \newline
%   ~~\meta{operator} \meta{number_2} \newline
%   |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
% \end{quote}
% The definition of \cs{@@_parse_continue:NwN} is then very simple:
% \begin{verbatim}
%   \cs_new:Npn \__fp_parse_continue:NwN #1#2@#3 { #3 #1 #2 @ }
% \end{verbatim}
% In the first case, |#3|~is \cs{use_none:n}, yielding
% \begin{quote}
%   \cs{use_none:n} \meta{precedence} \meta{number} |@| \newline
%   |\__fp_parse_infix_|\meta{operator}|:N|
% \end{quote}
% 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{quote}
%   \cs{@@_parse_apply_binary:NwNwN} \newline
%   ~~\meta{precedence} \meta{number} |@| \newline
%   ~~\meta{operator} \meta{number_2} \newline
%   |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
% \end{quote}
% then
% \begin{quote}
%   \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline
%   \cs{exp_after:wN} \meta{precedence} \newline
%   \cs{tex_romannumeral:D} |-`0| \newline
%   |\__fp_|\meta{operator}|_o:ww| \meta{number} \meta{number_2} \newline
%   \cs{tex_romannumeral:D} |-`0| \newline
%   |\__fp_parse_infix_|\meta{operator_2}|:N| \meta{precedence}
% \end{quote}
% 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 will 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_three|, 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_three 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 \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
%   is a digit
% \else:
%   not a digit
% \fi:
% \end{verbatim}
% To exclude |0|, replace \cs{c_nine} by \cs{c_ten}. 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 \__int_eval:w
%     ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26 = \c_three
%   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 $\{1, 2, 4, 10, 13\}$ will
% not work, 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 will not be expanded if we simply perform
% \texttt{f}-expansion.
% \begin{verbatim}
%   \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} }
%   \ExplSyntaxOff
%   \test { 1 + \X }
% \end{verbatim}
% Of course, spaces will 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 will stop
% the \texttt{f}-expansion before further macro calls are performed.
% However, in practice it should be enough: in particular, floating
% point numbers will correctly be 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, aux]{\@@_parse_operand:Nw}
%   \begin{syntax}
%     \cs{tex_romannumeral:D} \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, aux]{\@@_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, aux]{\@@_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}[aux, rEXP]{\@@_parse_expand:w}
%   \begin{syntax}
%     \cs{tex_romannumeral:D} \cs{@@_parse_expand:w} \meta{tokens}
%   \end{syntax}
%   This function must always come within a \tn{romannumeral} 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 { -`0 #1 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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}[aux, EXP]{\@@_type_from_scan:N, \@@_type_from_scan:w}
%   \begin{syntax}
%     \cs{@@_type_from_scan:N} \meta{token}
%   \end{syntax}
%   Grabs the pieces of the stringified \meta{token} which lies after
%   the first |s__fp|.  If the \meta{token} does not contain that
%   string, the result is |_?|.
%    \begin{macrocode}
\group_begin:
\char_set_catcode_other:N \S
\char_set_catcode_other:N \F
\char_set_catcode_other:N \P
\char_set_lccode:nn { `\- } { `\_ }
\tl_to_lowercase:n
  {
    \group_end:
    \cs_new:Npn \@@_type_from_scan:N #1
      {
        \exp_after:wN \@@_type_from_scan:w
        \token_to_str:N #1 \q_mark S--FP-? \q_mark \q_stop
      }
    \cs_new:Npn \@@_type_from_scan:w #1 S--FP #2 \q_mark #3 \q_stop {#2}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[rEXP, aux]
%   {
%     \@@_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
%   }
%   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{quote}
%     \meta{digits} |;| \meta{filling 0} |;| \meta{length}
%   \end{quote}
%   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 \c_nine < 1 \token_to_str:N ##1 \exp_stop_f:
          \token_to_str:N ##1 \exp_after:wN #2 \tex_romannumeral:D
        \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_nopar:Npn \@@_parse_digits_:N { ; ; 0 }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Parsing one number}
%
% \begin{macro}[aux, EXP]{\@@_parse_one:Nw}
%   This function finds one number, and packs the symbol which follows
%   in an \cs{infix_} 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 will be split further.  Despite the earlier
%   \texttt{f}-expansion, |#2|~may still be expandable if it was
%   protected by \cs{exp_not:N}, as happens with the \LaTeXe{} command
%   \tn{protect}.  Testing if |#2|~is a control sequence thus includes
%   \cs{exp_not:N}.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_one:Nw #1 #2
  {
    \if_catcode:w \scan_stop: \exp_not:N #2
      \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 \c_nine < 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}[aux, 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,
%   dispatched using \cs{@@_type_from_scan:N}.
%   \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 |\s__fp|, 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 |\s__fp_|\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.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_one_fp:NN #1#2
  {
    \cs:w @@_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end:
      {
        \exp_after:wN \@@_parse_infix:NN
        \exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w
      }
    #2
  }
\cs_new:Npn \@@_exp_after_mark_f:nw #1
  {
    \__msg_kernel_expandable_error:nn { kernel } { fp-early-end }
    \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1
  }
\cs_new:cpn { @@_exp_after_?_f:nw } #1#2
  {
    \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#2}
    \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 assume that it is a register, but
%   carefully unpacking 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{fp_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_eval:w} \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
    \tex_romannumeral:D -`0
      \exp_after:wN \@@_parse_one_register_aux:Nw
      \exp_after:wN #2
      \__int_value:w
        \exp_after:wN \@@_parse_exponent:N
        \tex_romannumeral:D \@@_parse_expand:w
  }
\group_begin:
\char_set_catcode_other:N \P
\char_set_catcode_other:N \T
\char_set_catcode_other:N \M
\char_set_catcode_other:N \U
\tl_to_lowercase:n
  {
    \group_end:
    \cs_new:Npn \@@_parse_one_register_aux:Nw #1
      {
        \exp_after:wN \use:nn
        \exp_after:wN \@@_parse_one_register_auxii:wwwNw
        \exp_after:wN { \tex_the:D \exp_not:N #1 }
          ; \@@_parse_one_register_dim:ww
          PT ; \@@_parse_one_register_mu:www
          . PT ; \@@_parse_one_register_int:www
          \q_stop
      }
    \cs_new:Npn \@@_parse_one_register_auxii:wwwNw
        #1 . #2 PT #3 ; #4#5 \q_stop { #4 #1.#2; }
    \cs_new:Npn \@@_parse_one_register_mu:www #1 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_eval:w #1 pt ;
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_one_digit:NN}
%   A digit marks the beginning of an explicit floating point number.
%   Once the number is found, we will 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
    \tex_romannumeral:D -`0
      \exp_after:wN \@@_sanitize:wN
      \int_use:N \__int_eval:w \c_zero \@@_parse_trim_zeros:N
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_one_other:NN}
%   For this function, |#2|~is a character token which is not a digit.
%   If it is a 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 - \c_thirty_two \fi: ) / 26
        = \c_three
      \exp_after:wN \@@_parse_word:Nw
      \exp_after:wN #1
      \exp_after:wN #2
      \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N
      \tex_romannumeral:D
    \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:
      \tex_romannumeral:D
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_word:Nw}
% \begin{macro}[aux, 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.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_word:Nw #1#2;
  {
    \cs_if_exist_use:cF { @@_parse_word_#2:N }
      {
        \__msg_kernel_expandable_error:nnn
          { kernel } { unknown-fp-word } {#2}
        \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0
        \@@_parse_infix:NN
      }
      #1
  }
\cs_new:Npn \@@_parse_letters:N #1
  {
    -`0
    \if_int_compare:w
        \if_catcode:w \scan_stop: \exp_not:N #1
          \c_zero
        \else:
          \__int_eval:w
            ( `#1 \if_int_compare:w `#1 > `Z - \c_thirty_two \fi: )
            / 26
        \fi:
        = \c_three
      \exp_after:wN #1
      \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N
      \tex_romannumeral:D
    \else:
      \@@_parse_return_semicolon:w #1
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux, 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 }
      {
        \__msg_kernel_expandable_error:nnn
          { kernel } { fp-missing-number } {#1}
        \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0
        \@@_parse_infix:NN #3 #1
      }
      {
        \__msg_kernel_expandable_error:nnn
          { kernel } { fp-unknown-symbol } {#1}
        \@@_parse_one:Nw #3
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Numbers: trimming leading zeros}
%
% Numbers will be 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}[aux, 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
      \tex_romannumeral:D
    \else:
      \if:w . \exp_not:N #1
        \exp_after:wN \@@_parse_strim_zeros:N
        \tex_romannumeral:D
      \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 \c_nine < 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}[aux, 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
      - \c_one
      \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D
    \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 \c_nine < 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}[aux, EXP]{\@@_parse_zero:}
%   After reading a significand of~$0$, we need to remove any exponent,
%   then put a sign of~|1| for \cs{@@_sanitize:wN}, small hack to denote
%   an exact zero (rather than an underflow).
%    \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}[aux, 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 will leave those digits in the \cs{__int_value:w}, and
%   grab some more, or stop 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_use:N \__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
        \tex_romannumeral:D \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 \cs{c_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 \c_zero
    \int_use:N \__int_eval:w 1
      \if_int_compare:w \c_nine < 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
          \tex_romannumeral:D
      \else:
        0000 0000 \@@_parse_exponent:Nw #4
      \fi:
      \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 |+\c_zero| or to |+\c_one|.
%   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 \c_nine < 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
      \tex_romannumeral:D
    \else:
      0 \@@_parse_exponent:Nw #4
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 (|+ \c_one|
%   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 + \c_one \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: + \c_one ; 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}[aux, 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
      \tex_romannumeral:D \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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_eight - #3
    \exp_after:wN \@@_parse_pack_leading:NNNNNww
    \int_use:N \__int_eval:w 1 #1
      \if_int_compare:w \c_nine < 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
          \tex_romannumeral:D
      \else:
        \if:w . \exp_not:N #4
          \exp_after:wN \@@_parse_small_leading:wwNN
          \__int_value:w 1
            \cs:w
              @@_parse_digits_
              \tex_romannumeral:D #3
              :N \exp_after:wN
            \cs_end:
            \tex_romannumeral:D
        \else:
          #2
          \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
          \exp_after:wN \c_zero
          \__int_value:w 1 0000 0000
          \@@_parse_exponent:Nw #4
        \fi:
      \fi:
      \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 \c_nine < 1 \token_to_str:N #4 \exp_stop_f:
      \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
      \exp_after:wN \c_eight
      \int_use:N \__int_eval:w 1 #1 \token_to_str:N #4
        \exp_after:wN \@@_parse_large_round:NN
        \exp_after:wN #4
        \tex_romannumeral:D
    \else:
      \exp_after:wN \@@_parse_pack_trailing:NNNNNNww
      \int_use:N \__int_eval:w \c_seven - #3 \exp_stop_f:
      \int_use:N \__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_
              \tex_romannumeral:D #3
              :N \exp_after:wN
            \cs_end:
            \tex_romannumeral:D
        \else:
          #2 0 \@@_parse_exponent:Nw #4
        \fi:
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Number: beyond 16 digits, rounding}
%
% \begin{macro}[aux, 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 |;\c_zero|,
%   otherwise by |;\c_one|.  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 \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
      + \c_one
      \if:w 0 \token_to_str:N #1
        \exp_after:wN \@@_parse_round_loop:N
        \tex_romannumeral:D
      \else:
        \exp_after:wN \@@_parse_round_up:N
        \tex_romannumeral:D
      \fi:
    \else:
      \@@_parse_return_semicolon:w \c_zero #1
    \fi:
    \@@_parse_expand:w
  }
\cs_new:Npn \@@_parse_round_up:N #1
  {
    \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
      + \c_one
      \exp_after:wN \@@_parse_round_up:N
      \tex_romannumeral:D
    \else:
      \@@_parse_return_semicolon:w \c_one #1
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 \cs{c_zero} or \cs{c_one} is added to the
%   surrounding integer expression.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_round_after:wN #1; #2
  {
    + #2 \exp_after:wN ;
    \int_use:N \__int_eval:w #1 + \@@_parse_exponent:N
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 will
%   expand to |+\c_zero| or |+\c_one|, 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 |+\c_zero| or |+\c_one| 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 \c_nine < 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_use:N \__int_eval:w
        \exp_after:wN \@@_parse_round_after:wN
        \int_use:N \__int_eval:w \c_zero * \__int_eval:w \c_zero
          \exp_after:wN \@@_parse_round_loop:N
          \tex_romannumeral:D
    \else:
      \@@_parse_exponent:Nw #2
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux, 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 \c_nine < 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_use:N \__int_eval:w
        \exp_after:wN \@@_parse_large_round_aux:wNN
        \int_use:N \__int_eval:w \c_one
          \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
      \tex_romannumeral:D
    \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_use:N \__int_eval:w #1
      \if:w . \exp_not:N #3
        + \c_zero * \__int_eval:w \c_zero
          \exp_after:wN \@@_parse_round_loop:N
          \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN ;
        \exp_after:wN \c_zero
        \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{verbatim}
%   \@@_parse:n { 3.2 erf(0.1) }
%   \@@_parse:n { 3.2 e\l_my_int }
%   \@@_parse:n { 3.2 \c_pi_fp }
% \end{verbatim}
% 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}[aux, 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}[aux, 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
      \tex_romannumeral:D
    \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
                \c_zero \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}[aux, 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
      \tex_romannumeral:D \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}[aux, 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 \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
      \token_to_str:N #1
      \exp_after:wN \@@_parse_exponent_digits:N
      \tex_romannumeral:D
    \else:
      \@@_parse_exponent_keep:NTF #1
        { \@@_parse_return_semicolon:w #1 }
        {
          \exp_after:wN ;
          \tex_romannumeral:D
        }
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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 \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
      \token_to_str:N #1
      \exp_after:wN \@@_parse_exponent_digits:N
      \tex_romannumeral:D
    \else:
      \@@_parse_return_semicolon:w #1
    \fi:
    \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, 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_x:nn { \s_@@ } { \exp_not:N #1 } = \c_zero
          0
          \__msg_kernel_expandable_error:nnn
            { kernel } { fp-after-e } { floating~point~ }
          \prg_return_true:
        \else:
          0
          \__msg_kernel_expandable_error:nnn
            { kernel } { bad-variable } {#1}
          \prg_return_false:
        \fi:
      \else:
        \if_int_compare:w
            \__str_if_eq_x:nn { \__int_value:w #1 } { \tex_the:D #1 }
            = \c_zero
          \__int_value:w #1
        \else:
          0
          \__msg_kernel_expandable_error:nnn
            { kernel } { fp-after-e } { dimension~#1 }
        \fi:
        \prg_return_false:
      \fi:
    \else:
      0
      \__msg_kernel_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, aux]{\@@_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}[aux, EXP]{\@@_parse_apply_unary: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
%   |\__fp_parse_infix_...:N| function.  We feed the data~|#2|, and the
%   argument~|#4|, to the function~|#3|, which expands
%   \cs{tex_romannumeral:D} thus the \texttt{infix} function~|#5|.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5
  {
    #3 #2 #4 @
    \tex_romannumeral:D -`0 #5 #1
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP, aux]{\@@_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_twelve} 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
        \tex_romannumeral:D
        \if_int_compare:w #2 < ##1
          \@@_parse_operand:Nw ##1
        \else:
          \@@_parse_operand:Nw #2
        \fi:
        \@@_parse_expand:w
      }
  }
\@@_tmp:w - \c_twelve \@@_set_sign_o:w 2
\@@_tmp:w ! \c_twelve \@@_not_o:w ?
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP, aux]{\@@_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
    \tex_romannumeral:D -`0
      \exp_after:wN \@@_sanitize:wN
      \int_use:N \__int_eval:w \c_zero \@@_parse_strim_zeros:N
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {\@@_parse_prefix_(:Nw, \@@_parse_lparen_after:NwN}
%   The left parenthesis is treated as a unary prefix operator because
%   it appears in exactly the same settings.  Commas will be allowed if
%   the previous precedence is $16$ (function with multiple arguments)
%   or $13$ (unary boolean \enquote{not}).  In this case, find an
%   operand using the precedence~$1$; otherwise the precedence~$0$.
%   Once the operand is found, the \texttt{lparen_after} auxiliary makes
%   sure that there was a closing parenthesis (otherwise it complains),
%   and leaves in the input stream the array it found as an operand,
%   fetching the following infix operator.
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N (
  \char_set_catcode_letter:N )
  \cs_new:Npn \@@_parse_prefix_(:Nw #1
    {
      \exp_after:wN \@@_parse_lparen_after:NwN
      \exp_after:wN #1
      \tex_romannumeral:D
      \if_int_compare:w #1 = \c_sixteen
        \@@_parse_operand:Nw \c_one
      \else:
        \@@_parse_operand:Nw \c_zero
      \fi:
      \@@_parse_expand:w
    }
  \cs_new:Npn \@@_parse_lparen_after:NwN #1#2 @ #3
    {
      \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N
        {
          \@@_exp_after_array_f:w #2 \s_@@_stop
          \exp_after:wN \@@_parse_infix:NN
          \exp_after:wN #1
          \tex_romannumeral:D \@@_parse_expand:w
        }
        {
          \__msg_kernel_expandable_error:nnn
            { kernel } { fp-missing } { ) }
          #2 @ \use_none:n #3
        }
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Constants}
%
% \begin{macro}[aux, 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_nopar:cpn { @@_parse_word_#1:N }
      { \exp_after:wN #2 \tex_romannumeral:D -`0 \@@_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}[aux, 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_nopar: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}[aux, 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_nopar:cpn { @@_parse_word_#1:N }
      {
        \exp_after:wN \@@_from_dim_test:ww
        \exp_after:wN 0 \exp_after:wN ,
        \__int_value:w \__dim_eval:w 1 #1 \exp_after:wN ;
        \tex_romannumeral:D -`0 \@@_parse_infix:NN
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Functions}
%
% ^^A begin[todo]
%
% ^^A todo: test <15 digits>1500000000.1
% ^^A todo: test <15 digits>1517263572.000
%
% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead"
%
% \begin{macro}[aux, 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
    \cs:w @@_#1_o:w \exp_after:wN \cs_end:
    \tex_romannumeral:D
    \@@_parse_operand:Nw \c_fifteen \@@_parse_expand:w
  }
\cs_new:Npn \@@_parse_function:NNN #1#2#3
  {
    \exp_after:wN \@@_parse_apply_unary:NNNwN
    \exp_after:wN #3
    \exp_after:wN #2
    \exp_after:wN #1
    \tex_romannumeral:D
    \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_word_acot:N , \@@_parse_word_acotd:N,
%     \@@_parse_word_atan:N , \@@_parse_word_atand:N,
%     \@@_parse_word_max:N  , \@@_parse_word_min:N  ,
%   }
%   Those functions are also unary (not binary), but may receive a
%   variable number of arguments.
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_parse_word_acot:N
  { \@@_parse_function:NNN \@@_acot_o:Nw \use_i:nn }
\cs_new_nopar:Npn \@@_parse_word_acotd:N
  { \@@_parse_function:NNN \@@_acot_o:Nw \use_ii:nn }
\cs_new_nopar:Npn \@@_parse_word_atan:N
  { \@@_parse_function:NNN \@@_atan_o:Nw \use_i:nn }
\cs_new_nopar:Npn \@@_parse_word_atand:N
  { \@@_parse_function:NNN \@@_atan_o:Nw \use_ii:nn }
\cs_new_nopar:Npn \@@_parse_word_max:N
  { \@@_parse_function:NNN \@@_minmax_o:Nw 2 }
\cs_new_nopar:Npn \@@_parse_word_min:N
  { \@@_parse_function:NNN \@@_minmax_o:Nw 0 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_word_abs:N   ,
%     \@@_parse_word_exp:N   ,
%     \@@_parse_word_ln:N    ,
%     \@@_parse_word_sqrt:N    ,
%   }
%   Unary functions.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_word_abs:N
  { \@@_parse_unary_function:nNN { set_sign } 0 }
\cs_new_nopar:Npn \@@_parse_word_exp:N
  { \@@_parse_unary_function:nNN {exp} ? }
\cs_new_nopar:Npn \@@_parse_word_ln:N
  { \@@_parse_unary_function:nNN {ln} ? }
\cs_new_nopar:Npn \@@_parse_word_sqrt:N
  { \@@_parse_unary_function:nNN {sqrt} ? }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_word_acos:N  ,
%     \@@_parse_word_acosd:N ,
%     \@@_parse_word_acsc:N  ,
%     \@@_parse_word_acscd:N ,
%     \@@_parse_word_asec:N  ,
%     \@@_parse_word_asecd:N ,
%     \@@_parse_word_asin:N  ,
%     \@@_parse_word_asind:N ,
%     \@@_parse_word_cos:N   ,
%     \@@_parse_word_cosd:N  ,
%     \@@_parse_word_cot:N   ,
%     \@@_parse_word_cotd:N  ,
%     \@@_parse_word_csc:N   ,
%     \@@_parse_word_cscd:N  ,
%     \@@_parse_word_sec:N   ,
%     \@@_parse_word_secd:N  ,
%     \@@_parse_word_sin:N   ,
%     \@@_parse_word_sind:N  ,
%     \@@_parse_word_tan:N   ,
%     \@@_parse_word_tand:N  ,
%   }
%   Unary functions.
%    \begin{macrocode}
\tl_map_inline:nn
  {
    {acos} {acsc} {asec} {asin}
    {cos} {cot} {csc} {sec} {sin} {tan}
  }
  {
    \cs_new_nopar:cpn { @@_parse_word_#1:N }
      { \@@_parse_unary_function:nNN {#1} \use_i:nn }
    \cs_new_nopar:cpn { @@_parse_word_#1d:N }
      { \@@_parse_unary_function:nNN {#1} \use_ii:nn }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_word_trunc:N,
%     \@@_parse_word_floor:N,
%     \@@_parse_word_ceil:N
%   }
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_parse_word_trunc:N
  { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_zero:NNN }
\cs_new_nopar:Npn \@@_parse_word_floor:N
  { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_ninf:NNN }
\cs_new_nopar:Npn \@@_parse_word_ceil:N
  { \@@_parse_function:NNN \@@_round_o:Nw \@@_round_to_pinf:NNN }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_word_round:N, \@@_parse_round:Nw}
%    \begin{macrocode}
\cs_new:Npn \@@_parse_word_round:N #1#2
  {
    \if_meaning:w + #2
      \@@_parse_round:Nw \@@_round_to_pinf:NNN
    \else:
      \if_meaning:w 0 #2
        \@@_parse_round:Nw \@@_round_to_zero:NNN
      \else:
        \if_meaning:w - #2
          \@@_parse_round:Nw \@@_round_to_ninf:NNN
        \fi:
      \fi:
    \fi:
    \@@_parse_function:NNN
      \@@_round_o:Nw \@@_round_to_nearest:NNN #1
    #2
  }
\cs_new:Npn \@@_parse_round:Nw
    #1 #2 \@@_round_to_nearest:NNN #3#4 { #2 #1 #3 }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Main functions}
%
% \begin{macro}[int, EXP]{\@@_parse:n}
% \begin{macro}[aux, EXP]{\@@_parse_after:ww}
%   Start a \tn{romannumeral} expansion so that \cs{@@_parse:n} expands
%   in two steps.  The \cs{@@_parse_operand:Nw} function will perform
%   computations until reaching an operation with precedence
%   \cs{c_minus_one} 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{c_zero}.
%    \begin{macrocode}
\cs_new:Npn \@@_parse:n #1
  {
    \tex_romannumeral:D
      \exp_after:wN \@@_parse_after:ww
      \tex_romannumeral:D
        \@@_parse_operand:Nw \c_minus_one
        \@@_parse_expand:w #1
        \s_@@_mark \@@_parse_infix_end:N
      \s_@@_stop
  }
\cs_new:Npn \@@_parse_after:ww
    #1@ \@@_parse_infix_end:N \s_@@_stop
  { \c_zero #1 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_operand:Nw}
% \begin{macro}[aux, EXP]{\@@_parse_continue:NwN}
%   The \cs{@@_parse_operand}
%   This is just a shorthand which sets up both \cs{@@_parse_continue}
%   and \cs{@@_parse_one} with the same precedence. Note the
%   trailing \cs{tex_romannumeral:D}. This function should be
%   used with much care.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_operand:Nw #1
  {
    -`0
    \exp_after:wN \@@_parse_continue:NwN
    \exp_after:wN #1
    \tex_romannumeral:D -`0
    \exp_after:wN \@@_parse_one:Nw
    \exp_after:wN #1
    \tex_romannumeral:D
  }
\cs_new:Npn \@@_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux, EXP]{\@@_parse_apply_binary:NwNwN}
%   Receives \meta{precedence} \meta{operand_1} |@| \meta{operation}
%   \meta{operand_2} |@| \meta{infix command}.  Builds the appropriate
%   call to the \meta{operation}~|#3|.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2@ #3 #4@ #5
  {
    \exp_after:wN \@@_parse_continue:NwN
    \exp_after:wN #1
    \tex_romannumeral:D -`0 \cs:w @@_#3_o:ww \cs_end: #2 #4
    \tex_romannumeral:D -`0 #5 #1
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Infix operators}
%
% \begin{macro}[aux, 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;
  }
\group_begin:
  \char_set_catcode_letter:N \*
  \cs_new:Npn \@@_parse_infix:NN #1 #2
    {
      \if_catcode:w \scan_stop: \exp_not:N #2
        \if_int_compare:w
            \__str_if_eq_x:nn { \s_@@_mark } { \exp_not:N #2 }
            = \c_zero
          \exp_after:wN \exp_after:wN
          \exp_after:wN \@@_parse_infix_mark:NNN
        \else:
          \exp_after:wN \exp_after:wN
          \exp_after:wN \@@_parse_infix_juxtapose:N
        \fi:
      \else:
        \if_int_compare:w
            \__int_eval:w
              ( `#2 \if_int_compare:w `#2 > `Z - \c_thirty_two \fi: )
              / 26
            = \c_three
          \exp_after:wN \exp_after:wN
          \exp_after:wN \@@_parse_infix_juxtapose:N
        \else:
          \exp_after:wN \@@_parse_infix_check:NNN
          \cs:w
            @@_parse_infix_ \token_to_str:N #2 :N
            \exp_after:wN \exp_after:wN \exp_after:wN
          \cs_end:
        \fi:
      \fi:
      #1
      #2
    }
  \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3
    {
      \if_meaning:w \scan_stop: #1
        \__msg_kernel_expandable_error:nnn
          { kernel } { fp-missing } { * }
        \exp_after:wN \@@_parse_infix_*:N
        \exp_after:wN #2
        \exp_after:wN #3
      \else:
        \exp_after:wN #1
        \exp_after:wN #2
        \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
      \fi:
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Closing parentheses and commas}
%
% \begin{macro}[aux, 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}[aux, 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}[aux, 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.
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N \)
  \cs_new:Npn \@@_parse_infix_):N #1
    {
      \if_int_compare:w #1 < \c_zero
        \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) }
        \exp_after:wN \@@_parse_infix:NN
        \exp_after:wN #1
        \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN @
        \exp_after:wN \use_none:n
        \exp_after:wN \@@_parse_infix_):N
      \fi:
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]+\@@_parse_infix_,:N+
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N \,
  \cs_new:Npn \@@_parse_infix_,:N #1
    {
      \if_int_compare:w #1 > \c_one
        \exp_after:wN @
        \exp_after:wN \use_none:n
        \exp_after:wN \@@_parse_infix_,:N
      \else:
        \if_int_compare:w #1 = \c_one
          \exp_after:wN \@@_parse_infix_comma:w
          \tex_romannumeral:D
        \else:
          \exp_after:wN \@@_parse_infix_comma_gobble:w
          \tex_romannumeral:D
        \fi:
        \@@_parse_operand:Nw \c_one
        \exp_after:wN \@@_parse_expand:w
      \fi:
    }
  \cs_new:Npn \@@_parse_infix_comma:w #1 @
    { #1 @ \use_none:n }
  \cs_new:Npn \@@_parse_infix_comma_gobble:w #1 @
    {
      \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma }
      @ \use_none:n
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Usual infix operators}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_infix_+:N, \@@_parse_infix_-:N,
%     \@@_parse_infix_/:N, \@@_parse_infix_mul:N,
%     \@@_parse_infix_and:N, \@@_parse_infix_or:N,
%   }
% \begin{macro}[aux, EXP]+\@@_parse_infix_^:N+ As described in the
%   \enquote{work plan}, each infix operator has an associated
%   \cs{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.
%
%   The odd requirement to set \cs{+} here is to cover the case where
%   \pkg{expl3} is loaded by plain \TeX{}: \cs{+} is an \cs{outer} macro there,
%   and so the following code would otherwise give an error in that case.
%    \begin{macrocode}
\group_begin:
%<*package>
  \cs_set_nopar:Npn \+ { }
%</package>
  \char_set_catcode_other:N \&
  \char_set_catcode_letter:N \^
  \char_set_catcode_letter:N \/
  \char_set_catcode_letter:N \-
  \char_set_catcode_letter:N \+
  \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
            \tex_romannumeral:D
            \@@_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:
        }
    }
  \@@_tmp:w \@@_parse_infix_^:N   ^ \c_fifteen \c_fourteen
  \@@_tmp:w \@@_parse_infix_/:N   / \c_ten     \c_ten
  \@@_tmp:w \@@_parse_infix_mul:N * \c_ten     \c_ten
  \@@_tmp:w \@@_parse_infix_-:N   - \c_nine    \c_nine
  \@@_tmp:w \@@_parse_infix_+:N   + \c_nine    \c_nine
  \@@_tmp:w \@@_parse_infix_and:N & \c_five    \c_five
  \@@_tmp:w \@@_parse_infix_or:N  | \c_four    \c_four
\group_end:
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Juxtaposition}
%
% \begin{macro}[aux, EXP]+\@@_parse_infix_(:N+
%   When an opening parenthesis appears where we expect an infix
%   operator, we compute the product of the previous operand and the
%   contents of the parentheses using \cs{@@_parse_infix_juxtapose:N}.
%    \begin{macrocode}
\cs_new:cpn { @@_parse_infix_(:N } #1
  { \@@_parse_infix_juxtapose:N #1 ( }
%    \end{macrocode}
% \end{macro}
%
% ^^A todo: can |...(1,2,3)pt| really occur?  If not, simplify.
% \begin{macro}[aux, EXP]
%   {\@@_parse_infix_juxtapose:N, \@@_parse_apply_juxtapose:NwwN}
%   Juxtaposition follows the same scheme as other binary operations,
%   but calls \cs{@@_parse_apply_juxtapose:NwwN} rather than directly
%   calling \cs{@@_parse_apply_binary:NwNwN}.  This lets us catch errors
%   such as |...(1,2,3)pt| where one operand of the juxtaposition is not
%   a single number: both |#3| and~|#5| of the \texttt{apply} auxiliary
%   must be empty.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_infix_juxtapose:N #1
  {
    \if_int_compare:w #1 < \c_ten
      \exp_after:wN @
      \exp_after:wN \@@_parse_apply_juxtapose:NwwN
      \tex_romannumeral:D
      \@@_parse_operand:Nw \c_ten
      \exp_after:wN \@@_parse_expand:w
    \else:
      \exp_after:wN @
      \exp_after:wN \use_none:n
      \exp_after:wN \@@_parse_infix_juxtapose:N
    \fi:
  }
\cs_new:Npn \@@_parse_apply_juxtapose:NwwN #1 #2;#3@ #4;#5@
  {
    \if_catcode:w ^ \tl_to_str:n { #3 #5 } ^
    \else:
      \@@_error:nffn { invalid-ii }
        { \@@_array_to_clist:n { #2; #3 } }
        { \@@_array_to_clist:n { #4; #5 } }
        { }
    \fi:
    \@@_parse_apply_binary:NwNwN #1 #2;@ * #4;@
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Multi-character cases}
%
% \begin{macro}[aux, EXP]{\@@_parse_infix_*:N}
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N ^
  \cs_new:cpn { @@_parse_infix_*:N } #1#2
    {
      \if:w * \exp_not:N #2
        \exp_after:wN \@@_parse_infix_^:N
        \exp_after:wN #1
      \else:
        \exp_after:wN \@@_parse_infix_mul:N
        \exp_after:wN #1
        \exp_after:wN #2
      \fi:
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[aux, EXP]+\@@_parse_infix_|:Nw+
% \begin{macro}[aux, EXP]+\@@_parse_infix_&:Nw+
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N \|
  \char_set_catcode_letter:N \&
  \cs_new:Npn \@@_parse_infix_|:N #1#2
    {
      \if:w | \exp_not:N #2
        \exp_after:wN \@@_parse_infix_|:N
        \exp_after:wN #1
        \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN \@@_parse_infix_or:N
        \exp_after:wN #1
        \exp_after:wN #2
      \fi:
    }
  \cs_new:Npn \@@_parse_infix_&:N #1#2
    {
      \if:w & \exp_not:N #2
        \exp_after:wN \@@_parse_infix_&:N
        \exp_after:wN #1
        \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN \@@_parse_infix_and:N
        \exp_after:wN #1
        \exp_after:wN #2
      \fi:
    }
\group_end:
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Ternary operator}
%
% \begin{macro}[aux, EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N}
%    \begin{macrocode}
\group_begin:
  \char_set_catcode_letter:N \?
  \cs_new:Npn \@@_parse_infix_?:N #1
    {
      \if_int_compare:w #1 < \c_three
        \exp_after:wN @
        \exp_after:wN \@@_ternary:NwwN
        \tex_romannumeral:D
        \@@_parse_operand:Nw \c_three
        \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN @
        \exp_after:wN \use_none:n
        \exp_after:wN \@@_parse_infix_?:N
      \fi:
    }
  \cs_new:Npn \@@_parse_infix_::N #1
    {
      \if_int_compare:w #1 < \c_three
        \__msg_kernel_expandable_error:nnnn
          { kernel } { fp-missing } { ? } { ~for~?: }
        \exp_after:wN @
        \exp_after:wN \@@_ternary_auxii:NwwN
        \tex_romannumeral:D
        \@@_parse_operand:Nw \c_two
        \exp_after:wN \@@_parse_expand:w
      \else:
        \exp_after:wN @
        \exp_after:wN \use_none:n
        \exp_after:wN \@@_parse_infix_::N
      \fi:
    }
\group_end:
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Comparisons}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_infix_<:N, \@@_parse_infix_=:N,
%     \@@_parse_infix_>:N, \@@_parse_infix_!:N
%   }
% \begin{macro}[aux, 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 \c_one
      \c_zero  \c_zero \c_zero \c_zero <
  }
\cs_new:cpn { @@_parse_infix_=:N } #1
  {
    \@@_parse_compare:NNNNNNN #1 \c_one
      \c_zero \c_zero \c_zero \c_zero =
  }
\cs_new:cpn { @@_parse_infix_>:N } #1
  {
    \@@_parse_compare:NNNNNNN #1 \c_one
      \c_zero \c_zero \c_zero \c_zero >
  }
\cs_new:cpn { @@_parse_infix_!:N } #1
  {
    \exp_after:wN \@@_parse_compare:NNNNNNN
    \exp_after:wN #1
    \exp_after:wN \c_zero
    \exp_after:wN \c_one
    \exp_after:wN \c_one
    \exp_after:wN \c_one
    \exp_after:wN \c_one
  }
\cs_new:Npn \@@_parse_excl_error:
  {
    \__msg_kernel_expandable_error:nnnn
      { kernel } { fp-missing } { = } { ~after~!. }
  }
\cs_new:Npn \@@_parse_compare:NNNNNNN #1
  {
    \if_int_compare:w #1 < \c_seven
      \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
          \if_catcode:w \scan_stop: \exp_not:N #7
            \c_minus_one
          \else:
            \__int_eval:w `#7 - `< \__int_eval_end:
          \fi:
         \@@_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
    \tex_romannumeral:D \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
    \tex_romannumeral:D
    \@@_parse_operand:Nw \c_seven \@@_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
          \c_zero
        \else:
          \if_case:w \@@_compare_back:ww #8 #2 \exp_stop_f:
            #5 \or: #6 \or: #7 \else: #4
          \fi:
        \fi:
      \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
      \tex_romannumeral:D -`0
      \@@_exp_after_o:w #3;
      \tex_romannumeral:D -`0
    \else:
      \exp_after:wN \@@_parse_continue:NwN
      \exp_after:wN #2
      \tex_romannumeral:D -`0
      \exp_after:wN #1
      \tex_romannumeral:D -`0
    \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{Candidate: defining new \pkg{l3fp} functions}
%
% \begin{macro}[EXP]{\fp_function:Nw}
%   Parse the argument of the function~|#1| using
%   \cs{@@_parse_operand:Nw} with a precedence of~$16$, and pass the
%   function and argument to \cs{@@_function_apply:nw}.
%    \begin{macrocode}
\cs_new:Npn \fp_function:Nw #1
  {
    \exp_after:wN \@@_function_apply:nw
    \exp_after:wN #1
    \tex_romannumeral:D
      \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\fp_new_function:Npn}
% \begin{macro}[aux]{\@@_new_function:NNnnn, \@@_new_function:Ncfnn}
% \begin{macro}[aux]{\@@_function_args:Nwn}
%   Save the code provided by the user in the control sequence
%   |\__fp_user_#1|.  Define |#1| to call \cs{@@_function_apply:nw}
%   after parsing one operand using \cs{@@_parse_operand:Nw} with
%   precedence~$16$.  The auxiliary \cs{@@_function_args:Nwn} receives
%   the user function and the number of arguments (half of the number of
%   tokens in the parameter text~|#2|), followed by the operand (as a
%   token list of floating points).  It checks the number of arguments,
%   and applies the user function to the arguments (without the outer
%   brace group).
%    \begin{macrocode}
\cs_new_protected:Npn \fp_new_function:Npn #1#2#
  {
    \@@_new_function:Ncfnn #1
      { @@_user_ \cs_to_str:N #1 }
      { \int_eval:n { \tl_count:n {#2} / \c_two } }
      {#2}
  }
\cs_new_protected:Npn \@@_new_function:NNnnn #1#2#3#4#5
  {
    \cs_new_nopar:Npn #1
      {
        \exp_after:wN \@@_function_apply:nw \exp_after:wN
          {
            \exp_after:wN \@@_function_args:Nwn
            \exp_after:wN #2
            \__int_value:w #3 \exp_after:wN ; \exp_after:wN
          }
        \tex_romannumeral:D
          \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w
      }
    \cs_new:Npn #2 #4 {#5}
  }
\cs_generate_variant:Nn \@@_new_function:NNnnn { Ncf }
\cs_new:Npn \@@_function_args:Nwn #1#2; #3
  {
    \int_compare:nNnTF { \tl_count:n {#3} } = {#2}
      { #1 #3 }
      {
        \__msg_kernel_expandable_error:nnnnn
          { kernel } { fp-num-args } { #1() } {#2} {#2}
        \c_nan_fp
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[aux, EXP]
%   {
%     \@@_function_apply:nw,
%     \@@_function_store:wwNwnn,
%     \@@_function_store_end:wnnn
%   }
%   The auxiliary \cs{@@_function_apply:nw} is called after parsing an
%   operand, so it receives some code~|#1|, then the operand ending
%   with~|@|, then a function such as \cs{@@_parse_infix_+:N} (but not
%   always of this form, see comparisons for instance).  Package the
%   operand (an array) into a token list with floating point items: this
%   is the role of \cs{@@_function_store:wwNwnn} and
%   \cs{@@_function_store_end:wnnn}.  Then apply \cs{@@_parse:n} to the
%   code~|#1| followed by a brace group with this token list.  This
%   results in a floating point result, which will correctly be parsed
%   as the next operand of whatever was looking for one.  The trailing
%   \cs{s_@@_mark} is used as a special infix operator to indicate that
%   the next token has already gone through \cs{@@_parse_infix:NN}.
%    \begin{macrocode}
\cs_new:Npn \@@_function_apply:nw #1#2 @
  {
    \@@_parse:n
      {
        \@@_function_store:wwNwnn #2
          \s_@@_mark \@@_function_store:wwNwnn ;
          \s_@@_mark \@@_function_store_end:wnnn
        \s_@@_stop { } { } {#1}
      }
    \s_@@_mark
  }
\cs_new:Npn \@@_function_store:wwNwnn
    #1; #2 \s_@@_mark #3#4 \s_@@_stop #5#6
  { #3 #2 \s_@@_mark #3#4 \s_@@_stop { #5 #6 } { { #1; } } }
\cs_new:Npn \@@_function_store_end:wnnn
    #1 \s_@@_stop #2#3#4
  { #4 {#2} }
%    \end{macrocode}
% \end{macro}
%
% ^^A end[todo]
%
% \subsection{Messages}
%
%    \begin{macrocode}
\__msg_kernel_new:nnn { kernel } { unknown-fp-word }
  { Unknown~fp~word~#1. }
\__msg_kernel_new:nnn { kernel } { fp-missing }
  { Missing~#1~inserted #2. }
\__msg_kernel_new:nnn { kernel } { fp-extra }
  { Extra~#1~ignored. }
\__msg_kernel_new:nnn { kernel } { fp-early-end }
  { Premature~end~in~fp~expression. }
\__msg_kernel_new:nnn { kernel } { fp-after-e }
  { Cannot~use~#1 after~'e'. }
\__msg_kernel_new:nnn { kernel } { fp-missing-number }
  { Missing~number~before~'#1'. }
\__msg_kernel_new:nnn { kernel } { fp-unknown-symbol }
  { Unknown~symbol~#1~ignored. }
\__msg_kernel_new:nnn { kernel } { fp-extra-comma }
  { Unexpected~comma:~extra~arguments~ignored. }
\__msg_kernel_new:nnn { kernel } { fp-num-args }
  { #1~expects~between~#2~and~#3~arguments. }
%    \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintChanges
%
% \PrintIndex
