From 134349701bddf7cbbacf6030c6b9f9838aff96fa Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Mon, 23 Jul 2012 17:15:00 +0000
Subject: l3kernel 3990 (17jul12)

git-svn-id: svn://tug.org/texlive/trunk@27108 c570f23f-e606-0410-a88d-b1316a301751
---
 .../source/latex/l3kernel/l3fp-parse.dtx           | 2601 ++++++++++++++++++++
 1 file changed, 2601 insertions(+)
 create mode 100644 Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx

(limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx')

diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
new file mode 100644
index 00000000000..11ec4e11b54
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
@@ -0,0 +1,2601 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-parse.dtx Copyright (C) 2011-2012 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>
+\RequirePackage{l3names}
+\GetIdInfo$Id: l3fp-parse.dtx 3986 2012-07-15 19:23:51Z joseph $
+  {L3 Floating-point expression parsing}
+\documentclass[full]{l3doc}
+\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
+%
+% ^^A begin[todo]
+%
+% ^^A To typeset the examples of expansion control, I'm using a hand-made
+% ^^A environment.
+% \newcommand{\fpOperation}[1]
+%   {\textcolor[rgb]{.6,.2,.2}{\ttfamily#1}}
+% \newcommand{\fpPrecedence}[1]
+%   {\textcolor[rgb]{.2,.2,.6}{\ttfamily#1}}
+% \newcommand{\fpExpand}[2]
+%   {\underline{\textcolor{red}{#1{#2}}}}
+% \newenvironment{l3fp-code-example}
+%   {\begin{quote}^^A
+%       \edef\^{\string^}^^A
+%       \let\*\fpExpand
+%       \let\o\fpOperation
+%       \let\p\fpPrecedence
+%       \def\!{\begingroup\def\!{\endgroup\par}\color[gray]{0.5}}^^A
+%       \ttfamily\frenchspacing
+%   }{\end{quote}}
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-parse} implementation}
+%
+%    \begin{macrocode}
+%<*initex|package>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=fp>
+%    \end{macrocode}
+%
+% \section{Precedences}
+%
+% In order of evaluation (some distinctions are irrelevant for the order
+% of evaluation, but serve as signals).
+% \begin{itemize}
+% \item[32] Juxtaposition for implicit multiplication.
+% \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 |%|.
+% \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}
+%
+% ^^A todo: change 'mantissa' => 'significand' everywhere.
+% ^^A todo: ask SO when sNaN can arise.
+%
+% \section{Evaluating an expression}
+%
+% \begin{macro}[EXP, int]{\@@_parse:n}
+%   \begin{syntax}
+%     \cs{@@_parse:n} \Arg{floating point expression}
+%   \end{syntax}
+%   This \texttt{f}-expands to the internal floating point number
+%   obtained by evaluating the \meta{floating point expression}.  During
+%   this 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.\footnote{Bruno: describe what
+%       happens in cases like $2\cs{c_three} = 6$.}
+%   \end{texnote}
+% \end{macro}
+%
+% \section{Work plan}\label{subsec:fp-parse-workplan}
+%
+% The task at hand is non-trivial, and some previous failed attempts have
+% shown me that the code ends up giving unreadable logs, so we'd better get
+% it (almost) right the first time. Let us thus first discuss precisely
+% the design before starting to write the code. To simplify matters,
+% we first consider expressions with integers only.
+%
+% \subsection{Storing results}
+%
+% The main issue in parsing expressions expandably is: \enquote{where
+%   in the input stream should the result be put?}
+%
+% One option is to place the result at the end of the expression,
+% but this has several drawbacks:
+% \begin{itemize}
+% \item firstly it means that for long expressions we would be reaching
+%   all the way to the end of the expression at every step of the
+%   calculation, which can be rather expensive;
+% \item secondly, when parsing parenthesized sub-expressions, we would
+%   naturally place the result after the corresponding closing parenthesis.
+%   But since \cs{@@_parse:n} does not assume that its argument is expanded,
+%   this closing parenthesis may be hidden in a macro, and not present yet,
+%   causing havoc.
+% \end{itemize}
+%
+% The other natural option is to store the result at the start of the
+% expression, and carry it as an argument of each macro. This does not
+% really work either: in order to expand what follows on the input stream,
+% we need to skip at each step over all the tokens in the result using
+% \cs{exp_after:wN}. But this requires adding many \cs{exp_after:wN} to
+% the result at each step, also an expensive process.
+%
+% Hence, we need to go for some fine expansion control: the result is
+% stored \emph{before} the start\ldots{} A toy model that illustrates this
+% idea is to try and add some positive integers which may be hidden
+% within macros, or registers. Assume that one number 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} -`0 \cs{clean:w} \meta{stuff}
+% \end{quote}
+% Hitting this construction by one step of expansion expands
+% \cs{exp_after:wN}, which triggers the primitive \cs{__int_value:w},
+% which reads an integer, \texttt{12345}. This integer is unfinished,
+% causing the second \cs{exp_after:wN} to expand, and trigger
+% the construction \cs{tex_romannumeral:D} |-`0|, which f-expands
+% \cs{clean:w} (see \pkg{l3expan.dtx} for an explanation). Assume
+% then that \cs{clean:w} is such that it expands \meta{stuff} to
+% \emph{e.g.}, |333444;|. Once \cs{clean:w} is done expanding, we
+% will obtain essentially
+% \begin{quote}\ttfamily
+%   \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; 333444 ;
+% \end{quote}
+% where in fact \cs{exp_after:wN} has already been expanded, and
+% \cs{__int_value:w} has already seen \texttt{12345}. Now,
+% \cs{__int_value:w} sees the \texttt{;}, and stops expanding, 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 \texttt{;}.
+%
+% On this toy example, we could note that 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} \texttt{12345}
+% \cs{exp_after:wN} \texttt{;} 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 to waste as little
+% as possible of this precious memory.
+%
+% \subsection{Precedence}
+%
+% A major point to keep in mind when parsing expressions is that
+% different operators have different precedence. The true analog
+% of our toy \cs{clean:w} macro must thus take care of that. For
+% definiteness, let us assume that the operation which prompted
+% \cs{clean:w} was a multiplication. Then \cs{clean:w} (expand
+% and) read digits until the number is ended by some operation.
+% If this is \texttt{+} or~\texttt{-}, then the multiplication
+% should be calculated next, so \cs{clean:w} can simply decide
+% that its job is done. However, if the operator we find is |^|,
+% then this operation must be performed before returning control
+% to the multiplication. This means that we need to \cs{clean:w}
+% the number following |^|, and perform the calculation, then just
+% end our job.
+%
+% Hence, each time a number is cleaned, the precedence of the
+% following operation must be compared to that of the previous
+% operation. The process of course has to happen recursively.
+% For instance, |1+2^3*4| would involve the following steps.
+% \begin{itemize}
+% \item |1| is cleaned up.
+% \item |2| is cleaned up.
+% \item The precedences of |+| and |^| are compared. Since the
+%   latter is higher, the second operand of |^| should be cleaned.
+% \item |3| is cleaned up.
+% \item The precedences of |^| and |*| are compared. Since the
+%   former is higher, the cleaning step stops.
+% \item Compute |2^3 = 8|.
+% \item We now have |1+8*4|, and the operation |+| is still
+%   looking for a second operand. Clean |8|.
+% \item The precedences of |+| and |*| are compared. Since the
+%   latter is higher, the second operand of |*| should be cleaned.
+% \item |4| is cleaned up, and the end of the expression is reached.
+% \item Compute |8*4 = 32|.
+% \item We now have |1+8*4|, and the operation |+| is still
+%   looking for a second operand. Clean |32|, and reach the end
+%   of the expression.
+% \item Compute |1+32 = 33|.
+% \end{itemize}
+% Here, there is some (expensive) redundant work: the results of
+% computations should not need to be cleaned again. Thus the true definition
+% is slightly more elaborate.
+%
+% The precedence of |(| and |)| are defined to be equal, and smaller than
+% the precedence of |+| and |-|, itself smaller than |*| and |/|, smaller,
+% finally, then the power operator |**| (or |^|).
+%
+%
+% \subsection{Infix operators}
+%
+% The implementation that was chosen is slightly wasteful: it causes
+% more nesting than necessary. ^^A todo: clarify.
+% However, it is simpler to implement and to explain than a slightly
+% optimized variant. ^^A todo: implement optimized version; compare.
+%
+% The cornerstone of that method is a pair of functions,
+% \cs{until} and \cs{one}, which both take as their first
+% argument the precedence (an integer) of the last operation.
+% The f-expansion of
+% \begin{quote}
+%   \cs{until} \meta{prec} \cs{one} \meta{prec} \meta{stuff}
+% \end{quote}
+% is the internal floating point obtained by \enquote{cleaning}
+% numbers which follow in the input stream, and performing
+% computations until reaching an operation with a precedence
+% less than or equal to \meta{prec}. This is followed by a control
+% sequence of the form \cs{infix_?}, namely,
+% \begin{quote}
+%   \meta{floating point} \cs{infix_?}
+% \end{quote}
+% where |?| is the operation following that number in the input
+% stream (we thus know that this operation has at most the
+% precedence \meta{prec}, otherwise it would have been performed
+% already).
+%
+% How is that expansion achieved? First, \cs{one} \meta{prec}
+% reads one \meta{floating point} number, and converts it to an
+% internal form, then the following operation, say |*|, is
+% packed in the form \cs{infix_*}, which is fed the \meta{prec}.
+% This function (one per infix operator) compares \meta{prec}
+% with the precedence of the operator we just read (here |*|).
+% If \meta{prec} is higher, our job is finished, and \cs{one}
+% leaves \cs{@@_parse_stop_until:N} so that \cs{until} knows to stop.
+% Otherwise, \cs{infix_*} triggers a new pair
+% \cs{until} \meta{prec(*)} \cs{one} \meta{prec(*)},
+% which produces the second operand \meta{floating point_2}
+% for the multiplication:
+% \begin{quote}
+%   \cs{until} \meta{prec} \meta{floating point} \newline
+%   \texttt{...} \meta{floating point_2} |;| \cs{infix_?}
+% \end{quote}
+% The dots are \cs{@@_parse_apply_binary:NwNwN} |*|.  The boolean
+% tells \cs{until} that it is not done, and it expands
+% (essentially) to
+% \begin{quote}
+%   \cs{until} \meta{prec}
+%   \cs{@@_mul_o:ww} \meta{floating point} \meta{floating point_2}
+%   \cs{tex_romannumeral:D} \texttt{-`0} \cs{infix_?} \meta{prec}
+% \end{quote}
+% making \TeX{} expand \cs{@@_mul_o:ww} before \cs{until}.  As
+% implemented in \pkg{l3fp-basics}, this operation expands what follows
+% its result exactly once. This triggers \cs{tex_romannumeral:D},
+% which fully expands \cs{infix_?} \meta{prec}. This compares
+% the precedence of the next operation, |?|, and \meta{prec},
+% and leaves a boolean (and possibly more things), which is then
+% checked by \cs{until} \meta{prec} to know if the result
+% of the multiplication is the end of the story, or if |?|
+% should be computed as well before \cs{until} \meta{prec} ends.
+%
+% This should be easier to see on an example. To each infix
+% operator, for instance, |*|, is associated the following data:
+% \begin{itemize}
+% \item a test function, \cs{infix_*}, which conditionally continues
+%   the calculation or waits to be hit again by expansion;
+% \item a function \fpOperation{*} (notation for \cs{@@_mul_o:ww})
+%   which performs the actual calculation;
+% \item an integer, \fpPrecedence{*}, which encodes the precedence of
+%   the operator.
+% \end{itemize}
+% The token that is currently being expanded is underlined,
+% and in red. Tokens that have not yet been read (and could
+% still be hidden in macros) are in gray.
+%
+% In a first reading, the disinction between the \meta{precedence}
+% \fpPrecedence{+}, the operation \fpOperation{+}, and the character
+% token |+| should not matter. It is only required to accomodate for
+% multi-token infix operators such as |**|: indeed, when controlling
+% expansion, we need to skip over those tokens using \cs{exp_after:wN},
+% and this only skips one token. Thus |**| needs to be replaced by a
+% single token (either its precedence or its calculating function,
+% depending on the place).
+%
+% To end the computation cleanly, we add a trailing right
+% parenthesis, and give |(| and |)| the lowest precedence,
+% so that \cs{until}\fpPrecedence{(} \cs{one}\fpPrecedence{(}
+% reads numbers and performs operations until meeting a right
+% parenthesis. This is discussed more precisely in the next section.
+%
+% \begin{l3fp-code-example}
+%   \cs{until}\p(    \*\cs{one}\p( \! 11 + 2**3 * 5 - 9 )\!
+%   \cs{until}\p( 1  \*\cs{one}\p( \! 1  + 2**3 * 5 - 9 )\!
+%   \cs{until}\p( 11 \*\cs{one}\p( \!    + 2**3 * 5 - 9 )\!
+%   \cs{until}\p( 11; \*\cs{infix_+}\p( \! 2**3 * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 2**3 * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2 \*\cs{one}\p+ \!  **3 * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; \*\cs{infix_**}\p+ \! 3 * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2;
+%       F \o{**} \cs{until}\p{**} \*\cs{one}\p{**} \! 3 * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2;
+%       F \o{**} \cs{until}\p{**} 3 \*\cs{one}\p{**} \! * 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2;
+%       F \o{**} \cs{until}\p{**} 3; \*\cs{infix_*}\p{**} \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 2;
+%       F \o{**} \*\cs{until}\p{**} 3; T \cs{infix_*} \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 2;
+%       F \o{**} 3; \cs{infix_*} \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{**} 2; 3;
+%       \cs{infix_*}\p+ \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; \*\cs{infix_*}\p+ \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 8;
+%       F \o* \cs{until}\p* \*\cs{one}\p* \! 5 - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 8;
+%       F \o* \cs{until}\p* 5 \*\cs{one}\p* \! - 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 8;
+%       F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 8;
+%       F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\!
+%   \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 8; F \o* 5; \cs{infix_-} \! 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{*} 8; 5; \cs{infix_-}\p+ \! 9 )\!
+%   \cs{until}\p( 11; F \o+ \cs{until}\p+ 40; \*\cs{infix_-}\p+ \! 9 )\!
+%   \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 40; T \cs{infix_-} \! 9 )\!
+%   \*\cs{until}\p( 11; F \o+ 40; \cs{infix_-} \! 9 )\!
+%   \cs{until}\p( \*\o{+} 11; 40; \cs{infix_-}\p( \! 9 )\!
+%   \cs{until}\p( 51; \*\cs{infix_-}\p( \! 9 )\!
+%   \cs{until}\p( 51; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\!
+%   \cs{until}\p( 51; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\!
+%   \cs{until}\p( 51; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\!
+%   \cs{until}\p( 51; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\!
+%   \*\cs{until}\p( 51; F \o- 9; \cs{infix_)} \!\!
+%   \cs{until}\p( \*\o{-} 51; 9; \cs{infix_)}\p( \!\!
+%   \cs{until}\p( 42; \*\cs{infix_)}\p( \!\!
+%   \*\cs{until}\p( 42; T \cs{infix_)} \!\!
+%   42; \cs{infix_)} \!\!
+% \end{l3fp-code-example}
+%
+% The only missing step is to clean the output by removing \cs{infix_)},
+% and possibly checking that nothing else remains.
+%
+% \subsection{Prefix operators, parentheses, and functions}
+%
+% Prefix operators (typically the 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 small subtelty on precedence
+% explained below. Once that argument is found, its sign can be
+% flipped. A left parenthesis is just a prefix operator which
+% removes the closing parenthesis (with some extra checks).
+%
+% Detecting prefix operators is done by \cs{one}. Before looking
+% for a number, it tests the first character. If it is a digit, a
+% dot, or a register, then we have a number. Otherwise, it is put
+% in a function, \cs{prefix_?} (where |?| is roughly that first
+% character), which is expanded. For instance, with a left
+% parenthesis we would have the following.
+% \begin{l3fp-code-example}
+%   \*\cs{one}\p* \! ( 2 + 3 ) \!
+%   \*\cs{prefix_(}\p* \! 2 + 3 ) \!
+%   \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) \!
+%   ... \!\!
+%   \o(\p* 5; \cs{infix_)} \! \!
+% \end{l3fp-code-example}
+% As usual, the \cs{until}--\cs{one} pair reads and compute
+% until reaching an operator of precedence at most \fpPrecedence{(}.
+% Then \fpOperation{(} removes \cs{infix_)} and looks ahead for
+% the next operation, comparing its precedence with the precedence
+% \fpPrecedence{*} of the previous operation (in fact, this comparison
+% is done by the relevant \cs{infix_?} built from the next operation).
+%
+% To support multi-character function (and constant) names, we
+% may need to put more than one character in the \cs{prefix_?}
+% construction. See implementation for details.
+%
+% Note that contrarily to \cs{infix_?} functions, the \cs{prefix_?}
+% functions perform no test on their argument (which is once more
+% the previous precedence), since we know that we need a number,
+% and must never stop there.
+%
+% Functions are implemented as prefix operators with infinitely high
+% precedence, so that their argument is the first number that can
+% possibly be built. For instance, something like the following could
+% happen in a computation
+% \begin{l3fp-code-example}
+%   \*\cs{one}\p* \! sqrt 4 + 3 ) \!
+%   \*\cs{prefix_sqrt}\p* \! 4 + 3 ) \!
+%   \o{sqrt}\p* \cs{until}\p{$\infty$} \*\cs{one}\p{$\infty$} \! 4 + 3 ) \!
+%   ... \!\!
+%   \o{sqrt}\p* 4; \cs{infix_+} \! 3 ) \!
+%   2; \*\cs{infix_+}\p* \! 3 ) \!
+% \end{l3fp-code-example}
+%
+% Lonely example, to be put somewhere: |2+sin 1 * 3| is $2+(\sin(1)\times 3)$.
+%
+% A further complication arises in the case of the unary |-| sign:
+% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. Easy, just give
+% |-| a lower precedence, equal to that of the infix |+| and |-|.
+% Unfortunately, this fails in subtle cases such as |3**-2*4|,
+% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$.
+% In fact, a unary |-| should only perform operations whose precedence
+% is greater than that of the last operation, as well as
+% |-|.\footnote{Taking into account the precedence of \texttt{-} itself
+%   only matters when it follows a left parenthesis:
+%   \texttt{(-2*4+3)} should give \texttt{((-8)+3)}, not \texttt{(-(8+3))}.}
+% Thus, \cs{prefix_-} \meta{prec} expands to something like
+% \begin{l3fp-code-example}
+%   \o- \meta{prec} \cs{until}\p? \*\cs{one} \p?
+% \end{l3fp-code-example}
+% where \fpPrecedence{?} is the maximum of \meta{prec} and the
+% precedence of |-|. Once the argument of |-| is found, \fpOperation{-}
+% gets its opposite, and leaves it for the previous operation to use.
+%
+% An example with parentheses.
+%
+% \begin{l3fp-code-example}
+%   \cs{until}\p(    \*\cs{one}\p( \! 11 * ( 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 1  \*\cs{one}\p( \! 1  * ( 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11 \*\cs{one}\p( \!    * ( 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11; \*\cs{infix_*}\p( \!   ( 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{one}\p* \! ( 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{prefix_(}\p* \! 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2 \*\cs{one}\p( \! + 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; \*\cs{infix_+}\p( \! 3 ) - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 3)-9)\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3 \*\cs{one}\p+ \! )-9)\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3; \*\cs{infix_)}\p+ \! -9)\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \*\cs{until}\p+ 3; T \cs{infix_)} \! -9)\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 2; F \o+ 3; \cs{infix_)} \! - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\o+ 2; 3; \cs{infix_)}\p( \! - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 5; \*\cs{infix_)}\p( \! - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 5; T \cs{infix_)} \! - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* \*\o(\p* 5; \cs{infix_)} \! - 9 )\!
+%   \cs{until}\p( 11; F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\!
+%   \cs{until}\p( 11; F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\!
+%   \*\cs{until}\p( 11; F \o* 5; \cs{infix_-} \! 9 )\!
+%   \cs{until}\p( \*\o* 11; 5; \cs{infix_-}\p( \! 9 )\!
+%   \cs{until}\p( 55; \* \cs{infix_-}\p( \! 9 )\!
+%   \cs{until}\p( 55; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\!
+%   \cs{until}\p( 55; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\!
+%   \cs{until}\p( 55; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\!
+%   \cs{until}\p( 55; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\!
+%   \*\cs{until}\p( 55; F \o- 9; \cs{infix_)} \!\!
+%   \cs{until}\p( \*\o- 55; 9; \cs{infix_)}\p( \!\!
+%   \cs{until}\p( 47; \*\cs{infix_)}\p( \!\!
+%   \*\cs{until}\p( 47; T \cs{infix_)} \!\!
+%   47; \cs{infix_)} \!\!
+% \end{l3fp-code-example}
+%
+% The end of this (sub)section was not revised yet
+%
+% \begin{itemize}
+% \item If it is a sign (|-| or |+|), then any following sign will be
+%   combined with this initial sign, forming \cs{prefix_+} or \cs{prefix_-}.
+% \item If it is a letter, then any following letter is grabbed, forming
+%   for instance \cs{prefix_sin} or \cs{prefix_sinh}.
+% \item Otherwise, only one token\footnote{Some support for multi-character
+%     prefix operator may be added in the future, but right now, I don't
+%     see a use for it. Perhaps, for including comments inside
+%     the computation itself??} is grabbed, for instance \cs{prefix_(}.
+% \end{itemize}
+%
+%^^A todo: make sure that's correct??
+%
+% Functions may take several arguments, possibly an unknown
+% number\footnote{Keyword argument support may be added later.},
+% for instance \texttt{round(1.23456,2)}.
+% \begin{itemize}
+% \item \texttt{round} is made into \cs{prefix_round}, which tries to
+%   grab one number using \cs{one}.
+% \item This builds \cs{prefix_(}, which uses \cs{one} to grab one
+%   number, calculating as necessary. The comma is given the same
+%   precedence as parentheses, and thus ends the calculation of the
+%   argument of \texttt{round}.
+% \item \texttt{round} now has its first argument. It can check whether
+%   the argument was closed by |,| or |)|, and branch accordingly.
+% \item If it was a comma, then the first argument is skipped over,
+%   through an expensive set of \cs{exp_after:wN}, and the second
+%   argument can be grabbed. Here it is simply an integer, easier
+%   to parse by building upon \cs{etex_numexpr:D}.
+% \item The closing parenthesis (or another comma) is seen, and the
+%   control is given back to \cs{prefix_round}.
+% \end{itemize}
+%
+% \subsection{Type detection}
+%
+% The type of data should be detected by reading the first few tokens,
+% before calling a type-specific function to parse it.  Or
+% should the type be obtained after the semicolon which indicates the
+% end of the thing? And placed there?
+%
+% ^^A todo: what did I mean in this paragraph?
+% Also to grab exponents correctly, build \cs{@@_<abc>:w} when seeing
+% some non-numeric |abc| while still looking to complete a number (or
+% other data). Then, if \cs{@@_postfix_<type>_<abc>:w} exists, use it.
+%
+% The internal representation of floating point numbers is quite
+% untypable, and we provide here the tools to convert from a more
+% user-friendly representation to internal floating point numbers,
+% and for various other conversions. Every floating point operation
+% calls those functions to normalize the input, so they must be
+% optimized.
+%
+% \section{Internal representation}
+%
+% Internally, a floating point number \meta{X} is a
+% token list containing
+% \begin{quote}
+%   \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \meta{body} |;|
+% \end{quote}
+% Let us explain each piece separately.
+%
+% Internal floating point numbers will be used in expressions,
+% and in this context will be subject to f-expansion. They must
+% leave a recognizable mark after \texttt{f}-expansion, to prevent the
+% floating point number from being re-parsed. Thus, \cs{s_@@}
+% is simply another name for \tn{relax}.
+%
+% Since floating point numbers are always accessed by the various
+% operations using f-expansion, we can safely let them be protected:
+% \texttt{x}-expansion will then leave them untouched.  However, when
+% used directly without an accessor function, floating points should
+% produce an error.  \cs{s_@@} will do nothing, and \cs{@@_chk:w}
+% produces an error.
+%
+% The (decimal part of the) IEEE-754-2008 standard requires the
+% format to be able to represent special floating point numbers
+% besides the usual positive and negative cases. The various
+% possibilities will be distinguished by their \meta{case}, which
+% is a single digit:\footnote{Bruno: I need to implement subnormal
+%   numbers. Also, quiet and signalling \texttt{nan} must be better
+%   distinguished.}
+% \begin{itemize}
+% \item[0] zeros: |+0| and |-0|,
+% \item[1] \enquote{normal} numbers (positive and negative),
+% \item[2] infinities: |+inf| and |-inf|,
+% \item[3] quiet and signalling \texttt{nan}.
+% \end{itemize}
+% The \meta{sign} is |0| (positive) or |2| (negative),
+% except in the case of \texttt{nan}, which have $\meta{sign} = 1$.
+% This ensures that changing the \meta{sign} digit to $2-\meta{sign}$
+% is exactly equivalent to changing the sign of the number.
+%
+% Special floating point numbers have the form
+% \begin{quote}
+%   \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \cs{s_@@_...} |;|
+% \end{quote}
+% where \cs{s_@@_...} is a scan mark carrying information about how the
+% number was formed (useful for debugging).
+%
+% Normal floating point numbers ($\meta{case} = 1$) have the form
+% \begin{quote}
+%   \cs{s_@@} \cs{@@_chk:w} 1 \meta{sign} \Arg{exponent}
+%   \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} |;|
+% \end{quote}
+% Here, the \meta{exponent} is an integer, at most
+% $\cs{c_@@_max_exponent_int} =
+%   \the\csname\detokenize{c__fp_max_exponent_int}\endcsname$
+% in absolute value.  The body consists in four
+% blocks of exactly $4$ digits, $ 0000 \leq \meta{X_i} \leq 9999$,
+% such that
+% \[
+% \meta{X}
+% = (-1)^{\meta{sign}} 10^{-\meta{exponent}}
+% \sum_{i=1}^{4} \meta{X_i} 10^{-4i}
+% \]
+% and such that the \meta{exponent} is minimal. This implies
+% $ 1000 \leq \meta{X_1} \leq 9999 $.
+%
+% \begin{table}\centering
+%   \caption{Internal representation of floating point numbers.}
+%   \label{tab:fp-convert-special}
+%   \begin{tabular}{ll}
+%     \toprule
+%     \multicolumn{1}{c}{Representation} & Meaning \\
+%     \midrule
+%     0 0 \cs{s_@@_...}  \texttt{;} & Positive zero.      \\
+%     0 2 \cs{s_@@_...}  \texttt{;} & Negative zero.      \\
+%     1 0 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;}
+%                                   & Positive floating point. \\
+%     1 2 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;}
+%                                   & Negative floating point. \\
+%     2 0 \cs{s_@@_...}  \texttt{;} & Positive infinity.  \\
+%     2 2 \cs{s_@@_...}  \texttt{;} & Negative infinity.  \\
+%     3 1 \cs{s_@@_...}  \texttt{;} & Quiet \texttt{nan}.        \\
+%     3 1 \cs{s_@@_...}  \texttt{;} & Signalling \texttt{nan}.   \\
+%     \bottomrule
+%   \end{tabular}
+% \end{table}
+%
+% \section{Internal parsing functions}
+%
+% \begin{macro}[EXP, int]{\@@_parse_until:Nw}
+%   \begin{syntax}
+%     \cs{tex_romannumeral:D} \cs{@@_parse_until:Nw} \meta{precedence} \cs{@@_parse_expand:w} \meta{tokens}
+%   \end{syntax}
+%   Reads the \meta{tokens}, performing every computation with a
+%   precedence higher than \meta{precedence}, then expands to
+%   \begin{syntax}
+%     \meta{objects} |@| \cs{@@_parse_infix_\meta{operation}:N} \ldots{}
+%   \end{syntax}
+%   where the \meta{op} is the first operation with a lower precedence,
+%   possibly \texttt{end}.
+% \end{macro}
+%
+% \begin{macro}[EXP, int]{\@@_parse_operand:Nw}
+%   \begin{syntax}
+%     \cs{@@_parse_operand:Nw} \meta{precedence} \ldots{}
+%   \end{syntax}
+%   If the following \meta{operation} has a precedence higher than
+%   \meta{precedence}, expands to
+%   \begin{syntax}
+%     \meta{object_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object_2} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{}
+%   \end{syntax}
+%   and otherwise expands to
+%   \begin{syntax}
+%     \meta{object} |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} \ldots{}
+%   \end{syntax}
+% \end{macro}
+%
+% \begin{macro}[EXP, int]{\@@_parse_infix_\meta{operation}:N}
+%   \begin{syntax}
+%     \cs{@@_parse_infix_\meta{operation}:N} \meta{precedence}
+%   \end{syntax}
+%   If the \meta{op} has a precedence higher than \meta{precedence}, expands to
+%   \begin{syntax}
+%     |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object} |@| \cs{@@_parse_infix_\meta{operation_2}:N}
+%   \end{syntax}
+%   Otherwise expands to
+%   \begin{syntax}
+%     |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N}
+%   \end{syntax}
+% \end{macro}
+%
+% ^^A end[todo]
+%
+% \subsection{Expansion control}
+%
+% At each step in reading a floating point expression, we wish to
+% perform \texttt{f}-expansion.  Normally, spaces stop this
+% \texttt{f}-expansion.  This can be problematic: for instance, the
+% macro |\X| below will not be expanded if we simply do
+% \texttt{f}-expansion.
+% \begin{verbatim}
+%   \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} }
+%   \ExplSyntaxOff
+%   \test { 1 + \X }
+% \end{verbatim}
+% 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.
+%
+%^^A begin[todo]
+% Floating point expressions should behave as much as possible like
+% \eTeX{}-based integer expressions and dimension expressions.  In
+% particular, full-expansion should be performed as the expression is
+% read, token by token, forcing the expansion of protected macros, and
+% ignoring spaces.
+%
+% Full expansion can be done with \cs{tex_romannumeral:D} |-`0|.
+% Unfortunately, this expansion is stopped by spaces.  Thus using simply
+% this will fail on |\fp_eval:n { 1 + ~ \l_tmpa_fp }| since the floating
+% point variable will not be expanded.  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.  We can avoid being
+% stopped by such explicit space characters (and by some braces) if we
+% add \cs{use:n} after~|-`0|.
+%
+% Testing if a character token |#1| is a digit can be done using
+% \begin{verbatim}
+% \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+%   true code
+% \else:
+%   false code
+% \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.
+%
+%^^A end[todo]
+%
+% \begin{macro}[rEXP, aux]{\@@_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}[EXP, aux]{\@@_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}
+%
+% \subsection{Fp object type}
+%
+% \begin{macro}[EXP, int]{\@@_type_from_scan:N}
+%   \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 empty.
+%    \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}
+%
+% \subsection{Reading digits}
+%
+% \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 operand}
+%
+% At the start of an expression, or just following a binary operation or
+% a function call, we are looking for an operand.  This can be an
+% explicit floating point number, a floating point variable, a \TeX{}
+% register, a function call such as \texttt{sin(3)}, a parenthesized
+% expression, \emph{etc.}  We distinguish the various cases by their
+% first token after \texttt{f}-expansion:
+% \begin{itemize}
+% \item \cs{tex_relax:D} in some form.  That can be an internal
+%   floating point, a premature end, or an unitialized register.
+% \item A register.  We interpret this as the significand of a floating
+%   point number.  This is subtely different from unpacking it, for
+%   instance, \texttt{\cs{c_minus_one}**2} gives $1$, while
+%   \texttt{-1**2} gives $-1$.
+% \item A digit, or a dot.  That marks the start of the significand for
+%   a floating point number.
+% \item A letter (lower or upper-case), which starts an identifier,
+%   either a constant or a function (possibly unknown).
+% \item |+|, |-|, or |!|, unary operators, which resume looking for a
+%   floating point number before acting on it.
+% \item |(|, which makes us parse a subexpression until the
+%   matching~|)|.
+% \item Other characters such as |'| or |"| may be given a meaning
+%   later.  Characters such as |*| or |/| have a meaning as infix
+%   operators but are not valid when we are looking for an operand: for
+%   instance, |3+*4| is not valid.
+% \end{itemize}
+% A category code test separates the first two cases from the others,
+% and they are further distinguished with a meaning test.  We then
+% single out digits.  Letters are detected using their character code.
+% All other characters are taken care of by building a csname from that
+% character and using it to continue parsing.  Unknown characters lead
+% to an error.
+%
+% \begin{macro}[int, EXP]{\@@_parse_operand:Nw}
+%   Function called \cs{one} at other places. It grabs one operand, and
+%   packs the symbol that follows in an \cs{infix_} csname.  |#1| is the
+%   previous \meta{precedence}, and |#2| the first character of the
+%   operand (already \texttt{f}-expanded).
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_operand:Nw #1 #2
+  {
+    \if_catcode:w \tex_relax:D #2
+      \if_meaning:w \tex_relax:D #2
+        \exp_after:wN \exp_after:wN
+        \exp_after:wN \@@_parse_operand_relax:NN
+      \else:
+        \exp_after:wN \exp_after:wN
+        \exp_after:wN \@@_parse_operand_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_operand_digit:NN
+      \else:
+        \exp_after:wN \exp_after:wN
+        \exp_after:wN \@@_parse_operand_other:NN
+      \fi:
+    \fi:
+    #1 #2
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% ^^A todo: rounding of negative dimensions is probably wrong.
+% \begin{macro}[aux, EXP]
+%   {\@@_parse_operand_register:NN, \@@_parse_operand_register_aux:www}
+%   Find the exponent following the register |#2|, then combine the
+%   value of |#2| (mapping |1pt| to $1$) with the exponent to produce a
+%   floating point number.
+%    \begin{macrocode}
+\group_begin:
+\char_set_catcode_other:N \P
+\char_set_catcode_other:N \T
+\tl_to_lowercase:n
+  {
+    \group_end:
+    \cs_new:Npn \@@_parse_operand_register:NN #1#2
+      {
+        \exp_after:wN \@@_parse_infix_after_operand:NwN
+        \exp_after:wN #1
+        \tex_romannumeral:D -`0
+          \exp_after:wN \@@_parse_operand_register_aux:www
+          \tex_the:D
+            \exp_after:wN #2
+            \exp_after:wN P
+            \exp_after:wN T
+            \exp_after:wN \q_stop
+          \__int_value:w \@@_parse_exponent:N
+      }
+    \cs_new:Npn \@@_parse_operand_register_aux:www #1 PT #2 \q_stop #3 ;
+      { \@@_parse:n { #1 e #3 } }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
+%   {\@@_parse_operand_relax:NN, \@@_parse_operand_relax_aux:wwnw} The
+%   argument is a token equal to \cs{tex_relax:D}.  This can be
+%   \cs{s_@@}, \cs{s_@@_mark}, or a badly initialized register.  We make
+%   sure that the last argument of \cs{@@_parse_infix:NN} is
+%   correctly expanded.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_operand_relax:NN #1#2
+  {
+      \@@_parse_operand_relax_aux:wwnw
+        #2 \s_@@_mark
+          {
+            \@@_exp_after_o:nw
+              {
+                \tex_romannumeral:D -`0
+                \exp_after:wN \@@_parse_infix:NN
+                \exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w
+              }
+            \s_@@
+          }
+        \s_@@ #2
+          {
+            \@@_error:n { Premature~end~in~fp~expression. }
+            \exp_after:wN \c_nan_fp
+            \tex_romannumeral:D -`0
+              \@@_parse_infix:NN #1
+              \s_@@_mark
+          }
+        \s_@@_mark
+          {
+            \@@_error:n { Erroneous~variable~#2 used! }
+            \exp_after:wN \c_nan_fp
+            \tex_romannumeral:D -`0
+              \exp_after:wN \@@_parse_infix:NN
+              \exp_after:wN #1
+              \tex_romannumeral:D \@@_parse_expand:w
+          }
+        \s_@@_mark \s_@@_stop
+  }
+\cs_new:Npn \@@_parse_operand_relax_aux:wwnw
+    #1 \s_@@ #2 \s_@@_mark #3 #4 \s_@@_mark \s_@@_stop { #3 }
+%    \end{macrocode}
+% \end{macro}
+%
+% ^^A begin[todo]
+%
+% \begin{macro}[aux, EXP]{\@@_parse_operand_other:NN}
+%   The interesting bit is \cs{@@_parse_operand_other:NN}. It separates
+%   letters from non-letters and builds the appropriate \cs{prefix}
+%   function. If it is not defined (is \cs{tex_relax:D}), make it
+%   a signalling \texttt{nan}. We don't look for an argument, as the
+%   unknown \enquote{prefix} can also be a (mistyped) constant such
+%   as \texttt{Inf}.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_operand_other:NN #1 #2
+  {
+    \if_int_compare:w
+        \__int_eval:w \tex_uccode:D `#2 / 26 = \c_three
+      \exp_after:wN \@@_parse_operand_other_word_aux:Nw
+      \exp_after:wN #1
+      \tex_romannumeral:D
+        \exp_after:wN \@@_parse_letters:NN
+        \exp_after:wN #2
+        \tex_romannumeral:D
+    \else:
+      \exp_after:wN \@@_parse_operand_other_prefix_aux:NNN
+      \exp_after:wN #1
+      \exp_after:wN #2
+      \cs:w @@_parse_prefix_#2:Nw \exp_after:wN \cs_end:
+      \tex_romannumeral:D
+    \fi:
+    \@@_parse_expand:w
+  }
+
+\cs_new:Npn \@@_parse_letters:NN #1#2
+  {
+    \exp_after:wN \c_zero
+    \exp_after:wN #1
+    \tex_romannumeral:D
+      \if_int_compare:w
+          \if_catcode:w \tex_relax:D #2
+            \c_zero
+          \else:
+            \__int_eval:w \tex_uccode:D `#2 / 26
+          \fi:
+          = \c_three
+        \exp_after:wN \@@_parse_letters:NN
+        \exp_after:wN #2
+        \tex_romannumeral:D
+        \exp_after:wN \@@_parse_expand:w
+      \else:
+        \exp_after:wN \c_zero
+        \exp_after:wN ;
+        \exp_after:wN #2
+      \fi:
+  }
+\cs_new:Npn \@@_parse_operand_other_word_aux:Nw #1 #2;
+  {
+    \cs_if_exist_use:cF { @@_parse_word_#2:N }
+      {
+        \__msg_expandable_error:n { Unknown~word~#2. }
+        \exp_after:wN \c_nan_fp
+        \tex_romannumeral:D -`0
+          \@@_parse_infix:NN
+      }
+      #1
+  }
+\cs_new_eq:NN \s_@@_unknown \tex_relax:D
+\cs_new:Npn \@@_parse_operand_other_prefix_aux:NNN #1#2#3
+  {
+    \if_meaning:w \tex_relax:D #3
+      \exp_after:wN \@@_parse_operand_other_prefix_unknown:NNN
+      \exp_after:wN #2
+    \fi:
+    #3 #1
+  }
+\cs_new:Npn \@@_parse_operand_other_prefix_unknown:NNN #1#2#3
+  {
+    \cs_if_exist:cTF { @@_parse_infix_#1:N }
+      {
+        \@@_error:n { Missing~number~before~'#1'. }
+        \exp_after:wN \c_nan_fp
+        \tex_romannumeral:D -`0
+          \@@_parse_infix:NN #3 #1
+      }
+      {
+        \@@_error:n { Unknown~symbol~#1~ignored. }
+        \@@_parse_operand:Nw #3
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% The following forms are accepted:
+% \begin{itemize}
+% \item
+% \item \meta{floating point}
+% \item \meta{integer} |.| \meta{decimal} |e| \meta{exponent}
+% \end{itemize}
+% In both cases, \meta{signs} is a (possibly empty) string of
+% |+| and |-| (with any category code\footnote{Bruno: except
+%   1, 2, 4, 10, 13, and those which cannot be tokens (0, 5, 9),
+%   so really, just 3, 6, 7, 8, 11, 12.}).\footnote{Bruno:
+%   test (and implement) non-other digits.}
+%
+% In the second form, the \meta{integer} is a sequence of digits,
+% whose length is not limited by constraints \TeX{}'s integer
+% registers. It stops at the first non-digit character. The
+% \meta{decimal} part is formed by all digits from the dot
+% (if it exists) until the first non-digit character. The
+% \meta{exponent} part has the form \meta{exponent sign}
+% \meta{exponent body}, where \meta{exponent sign} is any string
+% of |+| or |-|, and \meta{exponent body} is a string of digits,
+% stopping, as usual, at the first non-digit.
+%
+% Any missing part will take the appropriate default value.
+% \begin{itemize}
+% \item A missing \meta{exponent} is considered to be zero.
+% \item A number with no dot has zero decimal part.
+% \item An empty \meta{integer} part or decimal part is zero.
+% \end{itemize}
+%
+% Border cases:
+% \begin{itemize}
+% \item \texttt{e1} is considered as invalid input, and gives
+%   \texttt{qnan}.\footnote{Bruno: now just gives an error.}
+%   This will be important once parsing expressions is
+%   implemented, since \texttt{e-1} would be ambiguous otherwise.
+% \item \texttt{.e3} and \texttt{.} are zero.
+% \end{itemize}
+%
+% Bruno: expansion, not yet. Only f-expansion at the start, and
+% unpacking of registers after signs.
+%
+%
+% Work-plan.
+% \begin{itemize}
+% \item Remove any leading sign and build the \meta{sign} as we go.
+%   If the next character is a letter, go to the \enquote{special}
+%   branch, discussed later.
+% \item Drop leading zeros.
+% \item If the next character is a dot, drop some more zeros,
+%   keeping track of how many were dropped after the dot.
+%   Counting those gives $\meta{exp_1}<0$. Then read the decimal part
+%   with the \cs{@@_from_str_small} functions.
+% \item Otherwise, $\meta{exp_1}=0$, and first read the integer part,
+%   then the decimal part. This is implemented through the more
+%   elaborate \cs{@@_from_str_large} functions.
+% \item Continuing in the same line of expansion, read the exponent
+%   \meta{exp_2}.
+% \item Finally check that nothing is left.\footnote{Bruno: not done yet.}
+% \end{itemize}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_operand_digit:NN}
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_operand_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}
+%
+% ^^A end[todo]
+%
+% \subsubsection{Trimming leading zeros}
+%
+% \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 distinguished 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 #1
+      \exp_after:wN \@@_parse_trim_zeros:N
+      \tex_romannumeral:D
+    \else:
+      \if:w . #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.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_strim_zeros:N #1
+  {
+    \if:w 0 #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}
+%
+% \subsubsection{Exact zero}
+%
+% \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}, denoting an
+%   exact zero.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_zero:
+  {
+    \exp_after:wN ; \exp_after:wN 1
+    \__int_value:w \@@_parse_exponent:N
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{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 or to |+1|.  Otherwise, there is no
+%   $16$-th digit, so we put a $0$, and look for an exponent.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4
+  {
+    #1 #2
+    \if_int_compare:w \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 propagte this carry all
+%   the way up, the function \cs{@@_parse_pack_carry:w} increments the
+%   exponent, and changes the mantissa 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{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 . #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 . #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{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 --- and hopefully that is safe.
+%
+% \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 becase 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_ii: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 \texttt{e}, leave an exponent of $0$.  If
+%   there is an \texttt{e}, expand the next token to run some tests on
+%   it.  Namely, 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 #1
+      \exp_after:wN \@@_parse_exponent_ii:N
+      \tex_romannumeral:D
+    \else:
+      0 \@@_parse_return_semicolon:w #1
+    \fi:
+    \@@_parse_expand:w
+  }
+\cs_new:Npn \@@_parse_exponent_ii:N #1
+  {
+    \if_int_compare:w \if_catcode:w \tex_relax:D #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 - #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 don't 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 \tex_relax:D #1
+      \if_meaning:w \tex_relax:D #1
+        \if_int_compare:w \pdftex_strcmp:D { \s_@@ } { #1 } = \c_zero
+          0 \@@_error:n { Cannot~use~floating~point~after~'e'. }
+          \prg_return_true:
+        \else:
+          0 \@@_error:n { Erroneous~variable~#1 used. }
+          \prg_return_false:
+        \fi:
+      \else:
+        \if_int_compare:w
+            \pdftex_strcmp:D { \__int_value:w #1 } { \tex_the:D #1 }
+            = \c_zero
+          \__int_value:w #1
+        \else:
+          0 \@@_error:n { Cannot~use~a~dimension~(#1)~after~'e'. }
+        \fi:
+        \prg_return_false:
+      \fi:
+    \else:
+      0 \@@_error:n { Missing~exponent~after~'e'. }
+      \prg_return_true:
+    \fi:
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% ^^A begin[todo]
+% ^^A todo: \@@_sin:Nn should first _set, then \@@_sin:w,
+% ^^A both for speed, and error reporting.
+% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead"
+%
+% \subsubsection{Beyond 16 digits: rounding}
+%
+% \begin{macro}[int]{\@@_cfs_round_loop:N}
+%   Used both for \cs{@@_parse_small_round:NN} and
+%   \cs{@@_parse_large_round:NN}.
+%   Should appear after a \cs{__int_eval:w} |0|. Reads digits one by one,
+%   until reaching a non-digit. Adds |+1| for each digit. If all digits
+%   found are |0|, ends the \cs{__int_eval:w} by |;\c_zero|, otherwise
+%   by |;\c_one|. This is done by switching the loop to |round_up|
+%   at the first non-zero digit.
+%
+%    \begin{macrocode}
+\cs_new:Npn \@@_cfs_round_loop:N #1
+  {
+    \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+      + \c_one
+      \if:w 0 #1
+        \exp_after:wN \@@_cfs_round_loop:N
+        \tex_romannumeral:D
+      \else:
+        \exp_after:wN \@@_cfs_round_up:N
+        \tex_romannumeral:D
+      \fi:
+    \else:
+      \@@_parse_return_semicolon:w \c_zero #1
+    \fi:
+    \@@_parse_expand:w
+  }
+\cs_new:Npn \@@_cfs_round_up:N #1
+  {
+    \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+      + 1
+      \exp_after:wN \@@_cfs_round_up:N
+      \tex_romannumeral:D
+    \else:
+      \@@_parse_return_semicolon:w \c_one #1
+    \fi:
+    \@@_parse_expand:w
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[int]{\@@_parse_large_round:NN}
+%   \begin{syntax}
+%     \cs{@@_parse_large_round:NN} \meta{digit} \meta{more digits}
+%   \end{syntax}
+%   \meta{digit} is the digit that we are currently rounding (we only
+%   care whether it is even or odd).
+%
+%   The goal is to get \cs{c_zero} or \cs{c_one}, check for an exponent
+%   afterwards, and combine it to the number of digits before the decimal
+%   point (which we thus need to keep track of).
+%    \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_after:wNN
+        \int_use:N \__int_eval:w \c_one
+          \exp_after:wN \@@_cfs_round_loop:N
+    \else: %^^A could be dot, or e, or other
+      \exp_after:wN \@@_parse_large_round_dot_test:NNw
+      \exp_after:wN #1
+      \exp_after:wN #2
+    \fi:
+  }
+\cs_new:Npn \@@_parse_large_round_dot_test:NNw #1#2
+  {
+    \if:w . #2
+      \exp_after:wN \@@_parse_small_round:NN
+      \exp_after:wN #1
+      \tex_romannumeral:D
+    \else:
+      \@@_parse_exponent:Nw #2
+    \fi:
+    \@@_parse_expand:w
+  }
+%    \end{macrocode}
+%   \begin{syntax}
+%     \cs{@@_parse_large_round_after:wNN} \meta{exp} |;|
+%     ~~\meta{0 or 1} \meta{next~token}
+%   \end{syntax}
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_large_round_after:wNN #1 ; #2 #3
+  {
+    \if:w . #3
+      \exp_after:wN \@@_parse_large_round_after_ii:wN
+      \int_use:N \__int_eval:w #1 +
+        \c_zero * \__int_eval:w \c_zero
+          \exp_after:wN \@@_cfs_round_loop:N
+          \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
+    \else:
+      + #2
+      \exp_after:wN ;
+      \int_use:N \__int_eval:w #1 +
+        \exp_after:wN \@@_parse_exponent:N
+        \exp_after:wN #3
+    \fi:
+  }
+\cs_new:Npn \@@_parse_large_round_after_ii:wN #1 ; #2
+  {
+    + #2
+    \exp_after:wN ;
+    \int_use:N \__int_eval:w #1 +
+      \@@_parse_exponent:N
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}[int]{\@@_parse_small_round:NN}
+%   \begin{syntax}
+%     \cs{@@_parse_small_round:NN} \meta{digit} \meta{more digits}
+%   \end{syntax}
+%   \meta{digit} is the digit that we are currently rounding (we only
+%   care whether it is even or odd).
+%
+%   The goal is to get \cs{c_zero} or \cs{c_one}
+%    \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_small_round_after:wN
+        \int_use:N \__int_eval:w \c_zero
+          \exp_after:wN \@@_cfs_round_loop:N
+          \tex_romannumeral:D
+    \else:
+      \@@_parse_exponent:Nw #2
+    \fi:
+    \@@_parse_expand:w
+  }
+\cs_new:Npn \@@_parse_small_round_after:wN #1; #2
+  {
+    + #2 \exp_after:wN ;
+    \__int_value:w \@@_parse_exponent:N
+  }
+%    \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_until:Nw} function will perform
+%   computations until reaching an operation with precedence
+%   \cs{c_minus_one} or less.  Then check that there was indeed nothing
+%   left (this cannot happen), and stop the initial expansion with
+%   \cs{c_zero}.%^^A todo: simplify a bit.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse:n #1
+  {
+    \tex_romannumeral:D
+      \exp_after:wN \@@_parse_after:ww
+      \tex_romannumeral:D
+        \@@_parse_until:Nw \c_minus_one
+        \@@_parse_expand:w #1 \s_@@_mark
+      \s_@@_stop
+  }
+\cs_new:Npn \@@_parse_after:ww #1@ #2 \s_@@_stop
+  {
+%<assert>    \assert_str_eq:nn { #2 } { \@@_parse_infix_end:N \s_@@_mark }
+    \c_zero #1
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]{\@@_parse_until:Nw}
+% \begin{macro}[aux, EXP]{\@@_parse_until_test:NwN}
+%   The \cs{@@_parse_until}
+%   This is just a shorthand which sets up both \cs{@@_parse_until_test}
+%   and \cs{@@_parse_operand} 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_until:Nw #1
+  {
+    -`0
+    \exp_after:wN \@@_parse_until_test:NwN
+    \exp_after:wN #1
+    \tex_romannumeral:D -`0
+    \exp_after:wN \@@_parse_operand:Nw
+    \exp_after:wN #1
+    \tex_romannumeral:D
+  }
+\cs_new:Npn \@@_parse_until_test:NwN #1 #2 @ #3 { #3 #1 #2 @ }
+\cs_new:Npn \@@_parse_stop_until:N #1 { }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_parse_until_test:NwN}
+%   \begin{syntax}
+%     \cs{@@_parse_until_test:NwN} \meta{prec} \meta{fp} \meta{bool}
+%   \end{syntax}
+%   If \meta{bool} is true, then \meta{fp} is the floating
+%   point number that we are looking for (it ends with |;|),
+%   and this expands to \meta{fp}. If \meta{bool} is false,
+%   then the input stream actually looks like
+%   \begin{quote}
+%     \cs{@@_parse_until_test:NwN} \meta{prec} \meta{fp_1} \meta{false}
+%     \meta{oper} \meta{fp_2} \cs{infix_?}
+%   \end{quote}
+%   and we must feed \meta{prec} to \cs{infix_?}, and perform
+%   \meta{oper} on \meta{fp_1} and \meta{fp_2}: this
+%   triggers the expansion of \cs{infix_?} \meta{prec}, continuing
+%   the computation (or stopping). In that case, the function \cs{until}
+%   yields
+%   \begin{quote}
+%     \cs{@@_parse_until_test:NwN} \meta{prec}
+%     \meta{oper} \meta{fp_1} \meta{fp_2}
+%     \cs{tex_romannumeral:D} |-`0| \cs{infix_?} \meta{prec}
+%   \end{quote}
+%   expanding \meta{oper} next.
+%    \begin{macrocode}
+%    \end{macrocode}
+% \end{macro}
+%
+% ^^A 3.5\mydim e4**2
+% ^^A todo: add tests that catcode changes don't mess things up.
+%
+% \subsection{Main functions}
+%
+% \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 \tex_relax:D #2
+        \if_int_compare:w
+            \pdftex_strcmp:D { \s_@@_mark } { #2 }
+            = \c_zero
+          \exp_after:wN \exp_after:wN
+          \exp_after:wN \@@_parse_infix_end:N
+        \else:
+          \exp_after:wN \exp_after:wN
+          \exp_after:wN \@@_parse_infix_juxtapose:N
+        \fi:
+      \else:
+        \if_int_compare:w
+            \__int_eval:w \tex_uccode:D `#2 / 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_#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 \tex_relax:D #1
+        \__msg_expandable_error:n { Missing~*~inserted. }
+        \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}
+%
+% \begin{macro}[int, EXP]{\@@_parse_apply_binary:NwNwN}
+%
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2#3@ #4 #5#6@ #7
+  {
+    \exp_after:wN \@@_parse_until_test:NwN
+    \exp_after:wN #1
+    \tex_romannumeral:D -`0
+      \cs:w
+        @@
+        \@@_type_from_scan:N #2
+        \@@_type_from_scan:N #5
+        _ #4 _o:ww
+      \cs_end:
+      #2#3 #5#6
+    \tex_romannumeral:D -`0 #7 #1
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]
+%   {\@@_parse_apply_unary_array:NNwN, \@@_parse_apply_unary:NNwN}
+%   Here, |#2| is \emph{e.g.}, \cs{@@_neg_@@:w}, and expands once after the
+%   calculation.\footnote{Bruno: explain.}  The argument |#3| may be an
+%   array, so either we map through all its items, or we feed all items
+%   at once to the custom function.
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_apply_unary_array:NNwN #1#2#3@#4
+  {
+    #2 #3 @
+    \tex_romannumeral:D -`0 #4 #1
+  }
+\cs_new:Npn \@@_parse_apply_unary:NNwN #1#2#3@#4
+  {
+    #2 #3
+    \tex_romannumeral:D -`0 #4 #1
+  }
+\cs_new:Npn \@@_parse_unary_type:N #1
+  { \@@_type_from_scan:N #1 :w \cs_end: #1 }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Prefix operators}
+%
+% \subsubsection{Identifiers}
+%
+% \begin{macro}[aux, EXP]
+%   {
+%     \@@_parse_word_inf:N, \@@_parse_word_nan:N, \@@_parse_word_pi:N ,
+%     \@@_parse_word_deg:N, \@@_parse_word_em:N ,
+%     \@@_parse_word_ex:N , \@@_parse_word_in:N , \@@_parse_word_pt: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 ,
+%     \@@_parse_word_true:N , \@@_parse_word_false:N ,
+%   }
+%   A whole bunch of floating point numbers.
+%    \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
+\@@_tmp:w { pt } \c_one_fp
+\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 {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} }
+\tl_map_inline:nn { {em} {ex} }
+  {
+    \cs_new_nopar:cpn { @@_parse_word_#1:N }
+      {
+        \exp_after:wN \dim_to_fp:n \exp_after:wN
+          { \dim_use:N \__dim_eval:w 1 #1 \exp_after:wN }
+        \tex_romannumeral:D -`0 \@@_parse_infix:NN
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]
+%   {
+%     \@@_parse_word_abs:N   ,
+%     \@@_parse_word_cos:N   ,
+%     \@@_parse_word_cot:N   ,
+%     \@@_parse_word_exp:N   ,
+%     \@@_parse_word_ln:N    ,
+%     \@@_parse_word_sin:N   ,
+%     \@@_parse_word_tan:N   ,
+%   }
+%   Unary functions, which are applied to all of their arguments when
+%   receiving an array.
+%    \begin{macrocode}
+\tl_map_inline:nn { {abs} {cos} {cot} {exp} {ln} {sin} {tan} }
+  {
+    \cs_new:cpn { @@_parse_word_#1:N } ##1
+      {
+        \exp_after:wN \@@_parse_apply_unary:NNwN
+        \exp_after:wN ##1
+        \cs:w @@_ #1 \exp_after:wN \@@_parse_unary_type:N
+        \tex_romannumeral:D
+        \@@_parse_until:Nw \c_fifteen
+        \@@_parse_expand:w
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]
+%   {
+%     \@@_parse_word_max:N , \@@_parse_word_min:N   ,
+%     \@@_parse_word_mod:N ,
+%   }
+%   Those functions are also unary, but need to mix all of their
+%   arguments together.
+%    \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2
+  {
+    \cs_new:Npn #1 ##1
+      {
+        \exp_after:wN \@@_parse_apply_unary_array:NNwN
+        \exp_after:wN ##1
+        \exp_after:wN #2
+        \tex_romannumeral:D
+        \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w
+      }
+  }
+\@@_tmp:w \@@_parse_word_max:N \@@_max:w
+\@@_tmp:w \@@_parse_word_min:N \@@_min:w
+ % \@@_tmp:w \@@_parse_word_mod:N \@@_mod:w %^^A todo: not implemented!
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]{\@@_parse_word_round:N}
+%   This function expects one or two arguments.
+%    \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:
+    \exp_after:wN \@@_parse_apply_round:NNwN
+    \exp_after:wN #1
+    \exp_after:wN \@@_round_to_nearest:NNN
+    \tex_romannumeral:D
+    \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w #2
+  }
+\cs_new:Npn \@@_parse_round:Nw
+    #1 #2 \@@_round_to_nearest:NNN #3 \@@_parse_expand:w #4
+  { #2 #1 #3 \@@_parse_expand:w }
+\cs_new:Npn \@@_parse_apply_round:NNwN #1#2#3@#4
+  {
+    \if_case:w \__int_eval:w \@@_array_count:w #3@ - \c_one \__int_eval_end:
+         \@@_round:Nwn #2 #3 {0} \tex_romannumeral:D
+    \or: \@@_round:Nww #2 #3 \tex_romannumeral:D
+    \else:
+      \@@_error:n { round()~expects~1~or~2~arguments. }
+      \exp_after:wN \c_nan_fp \tex_romannumeral:D
+    \fi:
+    -`0 #4 #1
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Unary minus, plus, not}
+%
+% \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw}
+%   A unary |+| does nothing.
+%    \begin{macrocode}
+\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_operand:Nw
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
+%   Unary |-| is harder.
+%   Boolean not.
+%    \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2
+  {
+    \cs_new:cpn { @@_parse_prefix_#1:Nw } ##1
+      {
+        \exp_after:wN \@@_parse_apply_unary:NNwN
+        \exp_after:wN ##1
+        \cs:w @@_ #2 \exp_after:wN \@@_parse_unary_type:N
+        \tex_romannumeral:D
+        \if_int_compare:w \c_twelve < ##1
+          \@@_parse_until:Nw ##1
+        \else:
+          \@@_parse_until:Nw \c_twelve
+        \fi:
+        \@@_parse_expand:w
+      }
+  }
+\@@_tmp:w - { neg }
+\@@_tmp:w ! { not }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Other prefixes}
+%
+% \begin{macro}[int]{\@@_parse_prefix_(:Nw}
+%    \begin{macrocode}
+\group_begin:
+  \char_set_catcode_letter:N \)
+  \cs_new:cpn { @@_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_until:Nw \c_one
+      \else:
+        \@@_parse_until: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
+        {
+          \@@_parse_exp_after_array:wf #2 \s_@@_stop
+          \exp_after:wN \@@_parse_infix:NN
+          \exp_after:wN #1
+          \tex_romannumeral:D \@@_parse_expand:w
+        }
+        {
+          \@@_error:n { Missing~')'~inserted. }
+          #2 @ \@@_parse_stop_until:N #3
+        }
+    }
+\group_end:
+%    \end{macrocode}
+% \end{macro}
+%
+%^^A todo: rename to exp_after_array_f:w
+% \begin{macro}[int, EXP]{\@@_parse_exp_after_array:wf}
+%    \begin{macrocode}
+\cs_new:Npn \@@_parse_exp_after_array:wf #1
+  {
+    \cs:w @@ \@@_type_from_scan:N #1 _exp_after_f:nw \cs_end:
+      { \@@_parse_exp_after_array:wf }
+    #1
+  }
+\cs_new:Npn \@@_stop_exp_after_f:nw #1#2 { }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_parse_prefix_.:Nw}
+%   This function is called when a number starts with a dot.
+%    \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}
+%
+% \subsection{Infix operators}
+%
+%   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}.  The
+%   latter two are only needed when defining the \cs{infix} function.
+%    \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+  {
+    \cs_new:Npn #1 ##1
+      {
+        \if_int_compare:w ##1 < #3
+          \exp_after:wN @
+          \exp_after:wN \@@_parse_apply_binary:NwNwN
+          \exp_after:wN #2
+          \tex_romannumeral:D
+          \@@_parse_until:Nw #4
+          \exp_after:wN \@@_parse_expand:w
+        \else:
+          \exp_after:wN @
+          \exp_after:wN \@@_parse_stop_until:N
+          \exp_after:wN #1
+        \fi:
+      }
+  }
+%    \end{macrocode}
+%
+% \begin{macro}[int, EXP]
+%   {
+%     \@@_parse_infix_+:N, \@@_parse_infix_-:N,
+%     \@@_parse_infix_/:N, \@@_parse_infix_mul:N,
+%     \@@_parse_infix_and:N, \@@_parse_infix_or:N,
+%   }
+%   Using the general mechanism for arithmetic operations.
+%    \begin{macrocode}
+\group_begin:
+  \char_set_catcode_other:N \&
+  \@@_tmp:w \@@_parse_infix_juxtapose:N * \c_thirty_two \c_thirty_two
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_ / :N } / \c_ten \c_ten
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_mul:N } * \c_ten \c_ten
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_ - :N } - \c_nine \c_nine
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_ + :N } + \c_nine \c_nine
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_and:N } & \c_five \c_five
+  \exp_args:Nc \@@_tmp:w { @@_parse_infix_ or:N } | \c_four \c_four
+\group_end:
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]{\@@_parse_infix_*:N}
+% \begin{macro}[int, EXP]+\@@_parse_infix_^:N+
+%   The power operation must be associative in the opposite order from
+%   all others.  For this, we reverse the test, hence treating a
+%   \enquote{previous precedence} of \cs{c_fourteen} as less binding
+%   than |^|.
+%    \begin{macrocode}
+\group_begin:
+  \char_set_catcode_letter:N ^
+  \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen
+  \cs_new:cpn { @@_parse_infix_*:N } #1#2
+    {
+      \if:w * #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}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]+\@@_parse_infix_|:Nw+
+% \begin{macro}[int, 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 | #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 & #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}
+%
+% \begin{macro}[int, EXP]
+%   {
+%     \@@_parse_infix_<:N, \@@_parse_infix_=:N,
+%     \@@_parse_infix_>:N, \@@_parse_infix_!:N
+%   }
+% \begin{macro}[aux, EXP]
+%   {
+%     \@@_parse_infix_excl_aux:NN,
+%     \@@_parse_infix_excl_error:,
+%     \@@_infix_compare:N,
+%     \@@_parse_compare:NNNNNw,
+%     \@@_parse_compare_expand:NNNNNw,
+%     \@@_parse_compare_end:NNNN,
+%     \@@_compare:wNNNNw,
+%   }
+%    \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_<:N } #1
+  {
+    \@@_infix_compare:N #1 \c_one_fp
+      \c_zero_fp  \c_zero_fp \c_zero_fp \c_zero_fp <
+  }
+\cs_new:cpn { @@_parse_infix_=:N } #1
+  {
+    \@@_infix_compare:N #1 \c_one_fp
+      \c_zero_fp \c_zero_fp \c_zero_fp \c_zero_fp =
+  }
+\cs_new:cpn { @@_parse_infix_>:N } #1
+  {
+    \@@_infix_compare:N #1 \c_one_fp
+      \c_zero_fp \c_zero_fp \c_zero_fp \c_zero_fp >
+  }
+\cs_new:cpn { @@_parse_infix_!:N } #1
+  {
+    \exp_after:wN \@@_parse_infix_excl_aux:NN
+    \exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w
+  }
+\cs_new:Npn \@@_parse_infix_excl_aux:NN #1#2
+  {
+    \if_catcode:w \tex_relax:D #2
+      \@@_parse_infix_excl_error:
+    \else:
+      \if_int_compare:w `#2 > `? \exp_stop_f:
+        \@@_parse_infix_excl_error:
+      \else:
+        \if_int_compare:w `#2 < `< \exp_stop_f:
+          \@@_parse_infix_excl_error:
+        \fi:
+      \fi:
+    \fi:
+    \@@_infix_compare:N #1 \c_zero_fp
+      \c_one_fp \c_one_fp \c_one_fp \c_one_fp #2
+  }
+\cs_new:Npn \@@_parse_infix_excl_error:
+  { \__msg_expandable_error:n { Missing~relation~symbol~after~'!'. } }
+\cs_new:Npn \@@_infix_compare:N #1
+  {
+    \if_int_compare:w #1 < \c_seven
+      \exp_after:wN \@@_parse_compare:NNNNNw
+    \else:
+      \exp_after:wN @
+      \exp_after:wN \@@_parse_stop_until:N
+      \exp_after:wN \@@_infix_compare:N
+    \fi:
+  }
+\cs_new:Npn \@@_parse_compare:NNNNNw #1#2#3#4#5#6
+  {
+    \if_case:w
+          \if_catcode:w \tex_relax:D #6
+            \c_minus_one
+          \else:
+            \__int_eval:w `#6 - `< \__int_eval_end:
+          \fi:
+         \@@_parse_compare_expand:NNNNNw #1#1#3#4#5
+    \or: \@@_parse_compare_expand:NNNNNw #1#2#1#4#5
+    \or: \@@_parse_compare_expand:NNNNNw #1#2#3#1#5
+    \or: \@@_parse_compare_expand:NNNNNw #1#2#3#4#1
+    \else: \@@_parse_compare_end:NNNN #2#3#4#5#6
+    \fi:
+  }
+\cs_new:Npn \@@_parse_compare_expand:NNNNNw #1#2#3#4#5
+  {
+    \exp_after:wN \@@_parse_compare:NNNNNw
+    \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:NNNN #1#2#3#4#5 \fi:
+  {
+    \fi:
+    \exp_after:wN @
+    \exp_after:wN \@@_parse_apply_compare:NwNNNNwN
+    \exp_after:wN #1
+    \exp_after:wN #2
+    \exp_after:wN #3
+    \exp_after:wN #4
+    \tex_romannumeral:D
+    \@@_parse_until:Nw \c_seven \@@_parse_expand:w #5
+  }
+\cs_new:Npn \@@_parse_apply_compare:NwNNNNwN #1 #2@ #3#4#5#6 #7@ #8
+  {
+    \exp_after:wN \@@_parse_until_test:NwN
+    \exp_after:wN #1
+    \tex_romannumeral:D -`0
+      \exp_after:wN \exp_after:wN
+      \exp_after:wN \exp_after:wN
+      \exp_after:wN \exp_after:wN
+      \if_case:w \@@_compare:ww #2 #7 \exp_stop_f:
+             #4
+      \or:   #5
+      \or:   #6
+      \else: #3
+      \fi:
+    \tex_romannumeral:D -`0 #8 #1
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \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_until:Nw \c_three
+        \exp_after:wN \@@_parse_expand:w
+      \else:
+        \exp_after:wN @
+        \exp_after:wN \@@_parse_stop_until:N
+        \exp_after:wN \@@_parse_infix_?:N
+      \fi:
+    }
+  \cs_new:Npn \@@_parse_infix_::N #1
+    {
+      \if_int_compare:w #1 < \c_three
+        \__msg_expandable_error:n { Missing~'?'~inserted~for~'?:'. }
+        \exp_after:wN @
+        \exp_after:wN \@@_ternary_ii:NwwN
+        \tex_romannumeral:D
+        \@@_parse_until:Nw \c_two
+        \exp_after:wN \@@_parse_expand:w
+      \else:
+        \exp_after:wN @
+        \exp_after:wN \@@_parse_stop_until:N
+        \exp_after:wN \@@_parse_infix_::N
+      \fi:
+    }
+\group_end:
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, EXP]+\@@_parse_infix_):N+
+%   This one is a little bit odd: force every previous operator to end,
+%   regardless of the precedence.  This is very similar to
+%   \cs{@@_parse_infix_end:N}.
+%    \begin{macrocode}
+\group_begin:
+  \char_set_catcode_letter:N \)
+  \cs_new:Npn \@@_parse_infix_):N #1
+    {
+      \if_int_compare:w #1 < \c_zero
+        \@@_error:n { Extra~')'~ignored. }
+        \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 \@@_parse_stop_until:N
+        \exp_after:wN \@@_parse_infix_):N
+      \fi:
+    }
+\group_end:
+\cs_new:Npn \@@_parse_infix_end:N #1
+  { @ \@@_parse_stop_until:N \@@_parse_infix_end:N }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int, 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 \@@_parse_stop_until: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_until:Nw \c_one
+        \exp_after:wN \@@_parse_expand:w
+      \fi:
+    }
+  \cs_new:Npn \@@_parse_infix_comma:w #1 @
+    { #1 @ \@@_parse_stop_until:N }
+  \cs_new:Npn \@@_parse_infix_comma_gobble:w #1 @
+    {
+      \@@_error:n { Unexpected~comma:~extra~arguments~ignored. }
+      @ \@@_parse_stop_until:N
+    }
+\group_end:
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</initex|package>
+%    \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
\ No newline at end of file
-- 
cgit v1.2.3

