summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx2895
1 files changed, 1420 insertions, 1475 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
index a9e926bcaeb..3923238e387 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
@@ -1,6 +1,6 @@
% \iffalse meta-comment
%
-%% File: l3fp-parse.dtx Copyright (C) 2011-2012 The LaTeX3 Project
+%% File: l3fp-parse.dtx Copyright (C) 2011-2013 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
@@ -36,7 +36,7 @@
%
%<*driver>
\RequirePackage{l3bootstrap}
-\GetIdInfo$Id: l3fp-parse.dtx 4482 2013-04-24 21:05:12Z joseph $
+\GetIdInfo$Id: l3fp-parse.dtx 4604 2013-11-19 03:03:47Z bruno $
{L3 Floating-point expression parsing}
\documentclass[full]{l3doc}
\begin{document}
@@ -61,26 +61,6 @@
%
% \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}
@@ -97,661 +77,526 @@
%<@@=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: ask SO when sNaN can arise.
+% \subsection{Work plan}
%
-% \section{Evaluating an expression}
+% The task at hand is non-trivial, and some previous failed attempts
+% show that the code leads to unreadable logs, so we had better get it
+% (almost) right the first time. Let us first describe our goal, then
+% discuss the design precisely before writing any code.
%
% \begin{macro}[EXP, int]{\@@_parse:n}
% \begin{syntax}
-% \cs{@@_parse:n} \Arg{floating point expression}
+% \cs{@@_parse:n} \Arg{fpexpr}
% \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.
+% Evaluates the \meta{floating point expression} and leaves the result
+% in the input stream as an internal floating point number. This
+% function forms the basis of almost all public \pkg{l3fp} functions.
+% During evaluation, each token is fully \texttt{f}-expanded.
% \begin{texnote}
% Registers (integers, toks, etc.) are automatically unpacked,
-% without requiring a function such as \cs{int_use:N}. Invalid
+% 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$.}
+% unrecoverable low-level \TeX{} errors.
% \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:
+% Floating point expressions are composed of numbers, given in various
+% forms, infix operators, such as |+|, |**|, or~|,| (which joins two
+% numbers into a list), and prefix operators, such as the unary~|-|,
+% functions, or opening parentheses. Here is a list of precedences
+% which control the order of evaluation (some distinctions are
+% irrelevant for the order of evaluation, but serve as signals), from
+% the tightest binding to the loosest binding.
% \begin{itemize}
-% \item 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.
+% \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}
%
-% 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.
+% \subsubsection{Storing results}
+%
+% The main question in parsing expressions expandably is to decide where
+% to put the intermediate results computed for various subexpressions.
+%
+% One option is to store the values at the start of the expression, and
+% carry them together as the first argument of each macro. However, we
+% want to \texttt{f}-expand tokens one by one in the expression (as
+% \cs{int_eval:n} does), and with this approach, expanding the next
+% unread token forces us to jump with \cs{exp_after:wN} over every value
+% computed earlier in the expression. With this approach, the run-time
+% will grow at least quadratically in the length of the expression, if
+% not as its cube (inserting the \cs{exp_after:wN} is tricky and slow).
+%
+% A second option is to place those values at the end of the expression.
+% Then expanding the next unread token is straightforward, but this
+% still hits a performance issue: for long expressions we would be
+% reaching all the way to the end of the expression at every step of the
+% calculation. The run-time is again quadratic.
+%
+% A variation of the above attempts to place the intermediate results
+% which appear when computing a parenthesized expression near the
+% closing parenthesis. This still lets us expand tokens as we go, and
+% avoids performance problems as long as there are enough parentheses.
+% However, it would be much better to avoid requiring the closing
+% parenthesis to be present as soon as the corresponding opening
+% parenthesis is read: the closing parenthesis may still be hidden in a
+% macro yet to be expanded.
%
% Hence, we need to go for some fine expansion control: the result is
-% stored \emph{before} the start\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.
+% stored \emph{before} the start!
+%
+% Let us illustrate this idea in a simple model: adding positive
+% integers which may be resulting from the expansion of macros, or may
+% be values of registers. Assume that one number, say, $12345$, has
+% already been found, and that we want to parse the next number. The
+% current status of the code may look as follows.
% \begin{quote}\ttfamily
% \cs{exp_after:wN} \cs{add:ww}
% \cs{__int_value:w} 12345 \cs{exp_after:wN} ; \newline
-% \cs{tex_romannumeral:D} -`0 \cs{clean:w} \meta{stuff}
+% \cs{tex_romannumeral:D} |\operand: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
+% One step of expansion expands \cs{exp_after:wN}, which triggers the
+% primitive \cs{__int_value:w}, which reads the five digits we have
+% already found, |12345|. This integer is unfinished, causing the
+% second \cs{exp_after:wN} to expand, and to trigger the construction
+% \cs{tex_romannumeral:D}, which expands |\operand:w|, defined to read
+% what follows and make a number out of it, then leave \cs{c_zero}, the
+% number, and a semicolon in the input stream. Once |\operand:w| is
+% done expanding, we obtain essentially
% \begin{quote}\ttfamily
-% \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; 333444 ;
+% \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; \newline
+% \cs{tex_romannumeral:D} \cs{c_zero} 333444 ;
% \end{quote}
-% where in fact \cs{exp_after:wN} has already been expanded, 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
+% where in fact \cs{exp_after:wN} has already been expanded,
+% \cs{__int_value:w} has already seen |12345|, and
+% \cs{tex_romannumeral:D} is still looking for a number. It finds
+% \cs{c_zero}, hence expands to nothing. Now, \cs{__int_value:w} sees
+% the \texttt{;}, which cannot be part of a number. The expansion
+% stops, and we are left with
% \begin{quote}\ttfamily
% \cs{add:ww} 12345 ; 333444 ;
% \end{quote}
% which can safely perform the addition by grabbing two arguments
-% delimited by \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.
+% delimited by~|;|.
+%
+% If we were to continue parsing the expression, then the following
+% number should also be cleaned up before the next use of a binary
+% operation such as \cs{add:ww}. Just like \cs{__int_value:w} |12345|
+% \cs{exp_after:wN}~|;| expanded what follows once, we need \cs{add:ww}
+% to do the calculation, and in the process to expand the following
+% once. This is also true in our real application: all the functions of
+% the form \cs{@@_..._o:ww} expand what follows once. This comes at the
+% cost of leaving tokens in the input stack, and we will need to be
+% careful not to waste this memory. All of our discussion above is nice
+% but simplistic, as operations should not simply be performed in the
+% order they appear.
+%
+% \subsubsection{Precedence and infix operators}
+%
+% The various operators we will encounter have different precedences,
+% which influence the order of calculations: $1+2\times 3 = 1+(2\times
+% 3)$ because $\times$~has a higher precedence than~$+$. The true
+% analog of our macro |\operand:w| must thus take care of that. When
+% looking for an operand, it needs to perform calculations until
+% reaching an operator which has lower precedence than the one which
+% called |\operand:w|. This means that |\operand:w| must know what the
+% previous binary operator is, or rather, its precedence: we thus rename
+% it |\operand:Nw|. Let us describe as an example how the calculation
+% |41-2^3*4+5| will be done. Here, we abuse notations: the first
+% argument of |\operand:Nw| should be an integer constant (\cs{c_three},
+% \cs{c_nine}, \ldots{}) equal to the precedence of the given operator,
+% not directly the operator itself.
% \begin{itemize}
-% \item |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|.
+% \item Clean up~|41| and find~|-|. We call |\operand:Nw|~|-| to find
+% the second operand.
+% \item Clean up~|2| and find~|^|.
+% \item Compare the precedences of |-| and~|^|. Since the latter is
+% higher, we need to compute the exponentiation. For this, find the
+% second operand with a nested call to |\operand:Nw|~|^|.
+% \item Clean up~|3| and find~|*|.
+% \item Compare the precedences of |^| and~|*|. Since the former is
+% higher, |\operand:Nw|~|^| has found the second operand of the
+% exponentiation, which is computed: $2^{3} = 8$.
+% \item We now have |41+8*4+5|, and |\operand:Nw|~|-| is still
+% looking for a second operand for the subtraction. Is it~$8$?
+% \item Compare the precedences of |-| and~|*|. Since the latter is
+% higher, we are not done with~$8$. Call |\operand:Nw|~|*| to find
+% the second operand of the multiplication.
+% \item Clean up~|4|, and find~|-|.
+% \item Compare the precedences of |*| and~|-|. Since the former is
+% higher, |\operand:Nw|~|*| has found the second operand of the
+% multiplication, which is computed: $8*4 = 32$.
+% \item We now have |41+32+5|, and |\operand:Nw|~|-| is still looking
+% for a second operand for the subtraction. Is it~$32$?
+% \item Compare the precedences of |-| and~|+|. Since they are equal,
+% |\operand:Nw|~|-| has found the second operand for the
+% subtraction, which is computed: $41-32=9$.
+% \item We now have |9+5|.
% \end{itemize}
-% 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
+% The procedure above stops short of performing all computations, but
+% adding a surrounding call to |\operand:Nw| with a very low precedence
+% ensures that all computations will be performed before |\operand:Nw|
+% is done. Adding a trailing marker with the same very low precedence
+% prevents the surrounding |\operand:Nw| from going beyond the marker.
+%
+% The pattern above to find an operand for a given operator, is to find
+% one number and the next operator, then compare precedences to know if
+% the next computation should be done. If it should, then perform it
+% after finding its second operand, and look at the next operator, then
+% compare precedences to know if the next computation should be done.
+% This continues until we find that the next computation should not be
+% done. Then, we stop.
+%
+% We are now ready to get a bit more technical and describe which of the
+% \pkg{l3fp-parse} functions correspond to each step above.
+%
+% First, \cs{@@_parse_operand:Nw} is the |\operand:Nw| function above,
+% with small modifications due to expansion issues discussed later. We
+% denote by \meta{precedence} the argument of \cs{@@_parse_operand:Nw},
+% that is, the precedence of the binary operator whose operand we are
+% trying to find. The basic action is to read numbers from the input
+% stream. This is done by \cs{@@_parse_one:Nw}. A first approximation
+% of this function is that it reads one \meta{number}, performing no
+% computation, and finds the following binary \meta{operator}. Then it
+% expands to
% \begin{quote}
-% \cs{until} \meta{prec} \cs{one} \meta{prec} \meta{stuff}
+% \meta{number} \newline
+% ~~|\__fp_parse_infix_|\meta{operator}|:N| \meta{precedence}
% \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,
+% expanding the \texttt{infix} auxiliary before leaving the above in the
+% input stream.
+%
+% We now explain the \texttt{infix} auxiliaries. We need some
+% flexibility in how we treat the case of equal precedences: most often,
+% the first operation encountered should be performed, such as |1-2-3|
+% being computed as |(1-2)-3|, but |2^3^4| should be evaluated as
+% |2^(3^4)| instead. For this reason, and to support the equivalence
+% between |**| and~|^| more easily, each binary operator is converted to
+% a control sequence |\__fp_parse_infix_|\meta{operator}|:N| when it is
+% encountered for the first time. Instead of passing both precedences
+% to a test function to do the comparison steps above, we pass the
+% \meta{precedence} (of the earlier operator) to the \texttt{infix}
+% auxiliary for the following \meta{operator}, to know whether to
+% perform the computation of the \meta{operator}. If it should not be
+% performed, the \texttt{infix} auxiliary expands to
% \begin{quote}
-% \meta{floating point} \cs{infix_?}
+% |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
% \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:
+% and otherwise it calls \cs{@@_parse_operand:Nw} with the precedence of
+% the \meta{operator} to find its second operand \meta{number_2} and the
+% next \meta{operator_2}, and expands to
% \begin{quote}
-% \cs{until} \meta{prec} \meta{floating point} \newline
-% \texttt{...} \meta{floating point_2} |;| \cs{infix_?}
+% |@| \cs{@@_parse_apply_binary:NwNwN} \newline
+% ~~~~\meta{operator} \meta{number_2} \newline
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
% \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
+% The \texttt{infix} function is responsible for comparing precedences,
+% but cannot directly call the computation functions, because the first
+% operand \meta{number} is before the \texttt{infix} function in the
+% input stream. This is why we stop the expansion here and give control
+% to another function to close the loop.
+%
+% A definition of \cs{@@_parse_operand:Nw} \meta{precedence} with some
+% of the expansion control removed is
% \begin{quote}
-% \cs{until} \meta{prec}
-% \cs{@@_*_o:ww} \meta{floating point} \meta{floating point_2}
-% \cs{tex_romannumeral:D} \texttt{-`0} \cs{infix_?} \meta{prec}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline
+% \cs{exp_after:wN} \meta{precedence} \newline
+% \cs{tex_romannumeral:D} |-`0| \newline
+% ~~\cs{@@_parse_one:Nw} \meta{precedence}
% \end{quote}
-% making \TeX{} expand \cs{@@_*_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{@@_*_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 distinction between the \meta{precedence}
-% \fpPrecedence{+}, the operation \fpOperation{+}, and the character
-% token |+| should not matter. It is only required to accommodate 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 subtlety 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
+% This expands \cs{@@_parse_one:Nw} \meta{precedence} completely, which
+% finds a number, wraps the next \meta{operator} into an \texttt{infix}
+% function, feeds this function the \meta{precedence}, and expands it,
+% yielding either
+% \begin{quote}
+% \cs{@@_parse_continue:NwN} \meta{precedence} \newline
+% \meta{number} |@| \newline
+% \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{quote}
+% or
+% \begin{quote}
+% \cs{@@_parse_continue:NwN} \meta{precedence} \newline
+% \meta{number} |@| \newline
+% \cs{@@_parse_apply_binary:NwNwN} \newline
+% ~~\meta{operator} \meta{number_2} \newline
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{quote}
+% The definition of \cs{@@_parse_continue:NwN} is then very simple:
+% \begin{verbatim}
+% \cs_new:Npn \__fp_parse_continue:NwN #1#2@#3 { #3 #1 #2 @ }
+% \end{verbatim}
+% In the first case, |#3|~is \cs{use_none:n}, yielding
+% \begin{quote}
+% \cs{use_none:n} \meta{precedence} \meta{number} |@| \newline
+% |\__fp_parse_infix_|\meta{operator}|:N|
+% \end{quote}
+% then \meta{number} |@| |\__fp_parse_infix_|\meta{operator}|:N|. In
+% the second case, |#3|~is \cs{@@_parse_apply_binary:NwNwN}, whose role
+% is to compute \meta{number} \meta{operator} \meta{number_2} and to
+% prepare for the next comparison of precedences: first we get
+% \begin{quote}
+% \cs{@@_parse_apply_binary:NwNwN} \newline
+% ~~\meta{precedence} \meta{number} |@| \newline
+% ~~\meta{operator} \meta{number_2} \newline
+% |@| |\__fp_parse_infix_|\meta{operator_2}|:N|
+% \end{quote}
+% then
+% \begin{quote}
+% \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline
+% \cs{exp_after:wN} \meta{precedence} \newline
+% \cs{tex_romannumeral:D} |-`0| \newline
+% |\__fp_|\meta{operator}|_o:ww| \meta{number} \meta{number_2} \newline
+% \cs{tex_romannumeral:D} |-`0| \newline
+% |\__fp_parse_infix_|\meta{operator_2}|:N| \meta{precedence}
+% \end{quote}
+% where |\__fp_|\meta{operator}|_o:ww| computes \meta{number}
+% \meta{operator} \meta{number_2} and expands after the result, thus
+% triggers the comparison of the precedence of the \meta{operator_2} and
+% the \meta{precedence}, continuing the loop.
+%
+% We have introduced the most important functions here, and the next few
+% paragraphs will describe various subtleties.
+%
+% \subsubsection{Prefix operators, parentheses, and functions}
+%
+% Prefix operators (unary |-|, |+|,~|!|) and parentheses are taken care
+% of by the same mechanism, and functions (\texttt{sin}, \texttt{exp},
+% etc.) as well. Finding the argument of the unary~|-|, for instance,
+% is very similar to grabbing the second operand of a binary infix
+% operator, with a subtle precedence explained below. Once that operand
+% is found, the operator can be applied to it (for the unary~|-|, this
+% simply flips the sign). A left parenthesis is just a prefix operator
+% with a very low precedence equal to that of the closing parenthesis
+% (which is treated as an infix operator, since it normally appears just
+% after numbers), so that all computations are performed until the
+% closing parenthesis. The prefix operator associated to the left
+% parenthesis does not alter its argument, but it removes the closing
+% parenthesis (with some checks).
+%
+% Prefix operators are the reason why we only summarily described the
+% function \cs{@@_parse_one:Nw} earlier. This function is responsible
+% for reading in the input stream the first possible \meta{number} and
+% the next infix \meta{operator}. If what follows \cs{@@_parse_one:Nw}
+% \meta{precedence} is a prefix operator, then we must find the operand
+% of this prefix operator through a nested call to
+% \cs{@@_parse_operand:Nw} with the appropriate precedence, then apply
+% the operator to the operand found to yield the result of
+% \cs{@@_parse_one:Nw}. So far, all is simple.
+%
+% The unary operators |+|, |-|,~|!| complicate things a little bit:
+% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. This would easily
+% be done by giving~|-| a lower precedence, equal to that of the infix
+% |+| and~|-|. Unfortunately, this fails in cases such as |3**-2*4|,
+% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. A
+% second attempt would be to call \cs{@@_parse_operand:Nw} with the
+% \meta{precedence} of the previous operator, but |0>-2+3| is then
+% parsed as |0>-(2+3)|: the addition is performed because it binds more
+% tightly than the comparision which precedes~|-|. The correct approach
+% is for a unary~|-| to perform operations whose precedence is greater
+% than both that of the previous operation, and that of the unary~|-|
+% itself. The unary~|-| is given a precedence higher than
+% multiplication and division. This does not lead to any surprising
+% result, since $-(x/y) = (-x)/y$ and similarly for multiplication, and
+% it reduces the number of nested calls to \cs{@@_parse_operand:Nw}.
+%
+% Functions are implemented as prefix operators with very high
% precedence, so that their argument is the first number that can
-% possibly be built. 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
-%
+% possibly be built, except for juxtaposition.
+%
+% Note that contrarily to the \texttt{infix} functions discussed
+% earlier, the \texttt{prefix} functions do perform tests on the
+% previous \meta{precedence} to decide whether to find an argument or
+% not, since we know that we need a number, and must never stop there.
+%
+% \subsubsection{Numbers and reading tokens one by one}
+%
+% So far, we have glossed over one important point: what is a
+% \enquote{number}? A number is typically given in the form
+% \meta{significand}|e|\meta{exponent}, where the \meta{significand} is
+% any non-empty string composed of decimal digits and at most one
+% decimal separator (a period), the exponent
+% \enquote{\texttt{e}\meta{exponent}} is optional and is composed of an
+% exponent mark~|e| followed by a possibly empty string of signs
+% |+| or~|-| and a non-empty string of decimal digits. The
+% \meta{significand} can also be an integer, dimension, skip, or muskip
+% variable, in which case dimensions are converted from points (or mu
+% units) to floating points, and the \meta{exponent} can also be an
+% integer variable. Numbers can also be given as floating point
+% variables, or as named constants such as |nan|, |inf| or~|pi|. We may
+% add more types in the future.
+%
+% When \cs{@@_parse_one:Nw} is looking for a \enquote{number}, here is
+% what happens.
% \begin{itemize}
-% \item If 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_(}.
+% \item If the next token is a control sequence with the meaning of
+% \cs{scan_stop:}, it can be: \cs{s_@@}, in which case our job is
+% done, as what follows is an internal floating point number, or
+% \cs{s_@@_mark}, in which case the expression has come to an early
+% end, as we are still looking for a number here, or something else,
+% in which case we consider the control sequence to be a bad
+% variable resulting from \texttt{c}-expansion.
+% \item If the next token is a control sequence with a different
+% meaning, we assume that it is a register, unpack it with
+% \cs{tex_the:D}, and use its value (in \texttt{pt} for dimensions
+% and skips, \texttt{mu} for muskips) as the \meta{significand} of a
+% number: we look for an exponent.
+% \item If the next token is a digit, we remove any leading zeros,
+% then read a significand larger than~$1$ if the next character is a
+% digit, read a significand smaller than~$1$ if the next character
+% is a period, or we have found a significand equal to~$0$
+% otherwise, and look for an exponent.
+% \item If the next token is a letter, we collect more letters until
+% the first non-letter: the resulting word may denote a function
+% such as |asin|, a constant such as |pi| or be unknown. In the
+% first case, we call \cs{@@_parse_operand:Nw} to find the argument
+% of the function, then apply the function, before declaring that we
+% are done. Otherwise, we are done, either with the value of the
+% constant, or with the value |nan| for unknown words.
+% \item If the next token is anything else, we check whether it is a
+% known prefix operator, in which case \cs{@@_parse_operand:Nw}
+% finds its operand. If it is not known, then either a number is
+% missing (if the token is a known infix operator) or the token is
+% simply invalid in floating point expressions.
% \end{itemize}
-%
-%^^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)}.
+% Once a number is found, \cs{@@_parse_one:Nw} also finds an infix
+% operator. This goes as follows.
% \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}.
+% \item
+% \item If the next token is a control sequence, it could be the
+% end-marker \cs{s_@@_mark}, which has the lowest precedence, and
+% otherwise it is a case of juxtaposing numbers, such as
+% |2\c_three|, with an implied multiplication.
+% \item If the next token is a letter, it is also a case of
+% juxtaposition, as letters cannot be proper infix operators.
+% \item Otherwise (including in the case of digits), if the token is a
+% known infix operator, the appropriate
+% |\__fp_infix_|\meta{operator}|:N| function is built, and if it
+% does not exist, we complain. In particular, the juxtaposition
+% |\c_three 2| is disallowed.
% \end{itemize}
%
-% \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.
+% In the above, we need to test whether a character token~|#1| is a
+% digit:
+% \begin{verbatim}
+% \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+% is a digit
+% \else:
+% not a digit
+% \fi:
+% \end{verbatim}
+% To exclude |0|, replace \cs{c_nine} by \cs{c_ten}. The use of
+% \cs{token_to_str:N} ensures that a digit with any catcode is detected.
+% To test if a character token is a letter, we need to work with its
+% character code, testing if |`#1| lies in $[65,90]$ (uppercase letters)
+% or $[97,112]$ (lowercase letters)
+% \begin{verbatim}
+% \if_int_compare:w \__int_eval:w
+% ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26 = \c_three
+% is a letter
+% \else:
+% not a letter
+% \fi:
+% \end{verbatim}
+% At all steps, we try to accept all category codes: when |#1|~is kept
+% to be used later, it is almost always converted to category code other
+% through \cs{token_to_str:N}. More precisely, catcodes $\{3, 6, 7, 8,
+% 11, 12\}$ should work without trouble, but $\{1, 2, 4, 10, 13\}$ will
+% not work, and of course $\{0, 5, 9\}$ cannot become tokens.
%
-% \section{Internal representation}
+% Floating point expressions should behave as much as possible like
+% \eTeX{}-based integer expressions and dimension expressions. In
+% particular, \texttt{f}-expansion should be performed as the expression
+% is read, token by token, forcing the expansion of protected macros,
+% and ignoring spaces. One advantage of expanding at every step is that
+% restricted expandable functions can then be used in floating point
+% expressions just as they can be in other kinds of expressions.
+% Problematically, spaces stop \texttt{f}-expansion: for instance, the
+% macro~|\X| below will not be expanded if we simply perform
+% \texttt{f}-expansion.
+% \begin{verbatim}
+% \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} }
+% \ExplSyntaxOff
+% \test { 1 + \X }
+% \end{verbatim}
+% Of course, spaces will not appear in a code setting, but may very
+% easily come in document-level input, from which some expressions may
+% come. To avoid this problem, at every step, we do essentially what
+% \cs{use:f} would do: take an argument, put it back in the input
+% stream, then \texttt{f}-expand it. This is not a complete solution,
+% since a macro's expansion could contain leading spaces which will stop
+% the \texttt{f}-expansion before further macro calls are performed.
+% However, in practice it should be enough: in particular, floating
+% point numbers will correctly be expanded to the underlying \cs{s_@@}
+% \ldots{} structure. The \texttt{f}-expansion is performed by
+% \cs{@@_parse_expand:w}.
%
-% 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.
+% ^^A begin[todo]
%
-% 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).
+% \subsection{Main auxiliary functions}
%
-% 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{macro}[rEXP, aux]{\@@_parse_operand:Nw}
% \begin{syntax}
-% \cs{tex_romannumeral:D} \cs{@@_parse_until:Nw} \meta{precedence} \cs{@@_parse_expand:w} \meta{tokens}
+% \cs{tex_romannumeral:D} \cs{@@_parse_operand:Nw} \meta{precedence} \cs{@@_parse_expand:w}
% \end{syntax}
-% Reads the \meta{tokens}, performing every computation with a
-% precedence higher than \meta{precedence}, then expands to
+% Reads the \enquote{\ttfamily\ldots{}}, performing every computation
+% with a precedence higher than \meta{precedence}, then expands to
% \begin{syntax}
-% \meta{objects} |@| \cs{@@_parse_infix_\meta{operation}:N} \ldots{}
+% \meta{result} |@| |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
% \end{syntax}
-% where the \meta{op} is the first operation with a lower precedence,
-% possibly \texttt{end}.
+% where the \meta{operation} is the first operation with a lower
+% precedence, possibly \texttt{end}, and the
+% \enquote{\ttfamily\ldots{}} start just after the \meta{operation}.
% \end{macro}
%
-% \begin{macro}[EXP, int]{\@@_parse_operand:Nw}
+% \begin{macro}[EXP, aux]{\@@_parse_infix_+:N}
% \begin{syntax}
-% \cs{@@_parse_operand:Nw} \meta{precedence} \ldots{}
+% \cs{@@_parse_infix_+:N} \meta{precedence} \ldots{}
% \end{syntax}
-% If the following \meta{operation} has a precedence higher than
-% \meta{precedence}, expands to
+% If |+|~has a precedence higher than the \meta{precedence}, cleans up
+% a second \meta{operand} and finds the \meta{operation_2} which
+% follows, and expands to
% \begin{syntax}
-% \meta{object_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object_2} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{}
+% |@| \cs{@@_parse_apply_binary:NwNwN} |+| \meta{operand} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{}
% \end{syntax}
-% and otherwise expands to
+% Otherwise expands to
% \begin{syntax}
-% \meta{object} |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} \ldots{}
+% |@| \cs{use_none:n} \cs{@@_parse_infix_+:N} \ldots{}
% \end{syntax}
+% A similar function exists for each infix operator.
% \end{macro}
%
-% \begin{macro}[EXP, int]{\@@_parse_infix_\meta{operation}:N}
+% \begin{macro}[EXP, aux]{\@@_parse_one:Nw}
% \begin{syntax}
-% \cs{@@_parse_infix_\meta{operation}:N} \meta{precedence}
+% \cs{@@_parse_one:Nw} \meta{precedence} \ldots{}
% \end{syntax}
-% If the \meta{op} has a precedence higher than \meta{precedence}, expands to
+% Cleans up one or two operands depending on how the precedence of the
+% next operation compares to the \meta{precedence}. If the following
+% \meta{operation} has a precedence higher than \meta{precedence},
+% expands to
% \begin{syntax}
-% |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object} |@| \cs{@@_parse_infix_\meta{operation_2}:N}
+% \meta{operand_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{operand_2} |@| |\__fp_parse_infix_|\meta{operation_2}|:N| \ldots{}
% \end{syntax}
-% Otherwise expands to
+% and otherwise expands to
% \begin{syntax}
-% |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N}
+% \meta{operand} |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operation}|:N| \ldots{}
% \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]
+% \subsection{Helpers}
%
% \begin{macro}[aux, rEXP]{\@@_parse_expand:w}
% \begin{syntax}
@@ -776,8 +621,6 @@
% \end{macrocode}
% \end{macro}
%
-% \subsection{Fp object type}
-%
% \begin{macro}[aux, EXP]{\@@_type_from_scan:N, \@@_type_from_scan:w}
% \begin{syntax}
% \cs{@@_type_from_scan:N} \meta{token}
@@ -804,8 +647,6 @@
% \end{macrocode}
% \end{macro}
%
-% \subsection{Reading digits}
-%
% \begin{macro}[rEXP, aux]
% {
% \@@_parse_digits_vii:N ,
@@ -817,11 +658,11 @@
% \@@_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
+% \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}
@@ -856,64 +697,37 @@
% \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 uninitialized register.
-% \item A register. We interpret this as the significand of a floating
-% point number. This is subtly 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).
+% \subsection{Parsing one number}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_one:Nw}
+% This function finds one number, and packs the symbol which follows
+% in an \cs{infix_} csname. |#1|~is the previous \meta{precedence},
+% and |#2|~the first token of the operand. We distinguish four cases:
+% |#2|~is equal to \cs{scan_stop:} in meaning, |#2|~is a different
+% control sequence, |#2|~is a digit, and |#2|~is something else (this
+% last case will be split further. Despite the earlier
+% \texttt{f}-expansion, |#2|~may still be expandable if it was
+% protected by \cs{exp_not:N}, as happens with the \LaTeXe{} command
+% \tn{protect}. Testing if |#2|~is a control sequence thus includes
+% \cs{exp_not:N}.
% \begin{macrocode}
-\cs_new:Npn \@@_parse_operand:Nw #1 #2
+\cs_new:Npn \@@_parse_one:Nw #1 #2
{
- \if_catcode:w \tex_relax:D #2
- \if_meaning:w \tex_relax:D #2
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if_meaning:w \scan_stop: #2
\exp_after:wN \exp_after:wN
- \exp_after:wN \@@_parse_operand_relax:NN
+ \exp_after:wN \@@_parse_one_fp:NN
\else:
\exp_after:wN \exp_after:wN
- \exp_after:wN \@@_parse_operand_register:NN
+ \exp_after:wN \@@_parse_one_register:NN
\fi:
\else:
\if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f:
\exp_after:wN \exp_after:wN
- \exp_after:wN \@@_parse_operand_digit:NN
+ \exp_after:wN \@@_parse_one_digit:NN
\else:
\exp_after:wN \exp_after:wN
- \exp_after:wN \@@_parse_operand_other:NN
+ \exp_after:wN \@@_parse_one_other:NN
\fi:
\fi:
#1 #2
@@ -921,111 +735,163 @@
% \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_exp_after_f:nw,
-% \@@_parse_exp_after_mark_f:nw,
-% \@@_parse_exp_after_?_f:nw
+% \@@_parse_one_fp:NN,
+% \@@_exp_after_mark_f:nw,
+% \@@_exp_after_?_f:nw
% }
-% The second argument is a control sequence equal to \cs{tex_relax:D}.
-% There are three cases, dispatched using \cs{@@_type_from_scan:N}.
+% This function receives a \meta{precedence} and a control sequence
+% equal to \cs{scan_stop:} in meaning. There are three cases,
+% dispatched using \cs{@@_type_from_scan:N}.
% \begin{itemize}
% \item \cs{s_@@} starts a floating point number, and we call
-% \cs{@@_parse_exp_after_f:nw}, which |f|-expands after the
-% floating point.
+% \cs{@@_exp_after_f:nw}, which |f|-expands after the floating
+% point.
% \item \cs{s_@@_mark} is a premature end, we call
-% \cs{@@_parse_exp_after_mark_f:nw}, which triggers the
-% appropriate error.
+% \cs{@@_exp_after_mark_f:nw}, which triggers an |fp-early-end|
+% error.
% \item For a control sequence not containing |\s__fp|, we call
-% \cs{@@_parse_exp_after_?_f:nw}, causing a |bad-variable| error.
+% \cs{@@_exp_after_?_f:nw}, causing a |bad-variable| error.
% \end{itemize}
% This scheme is extensible: additional types can be added by starting
% the variables with a scan mark of the form |\s__fp_|\meta{type} and
-% defining |\__fp_parse_exp_after_|\meta{type}|_f:nw|. In all cases, we
-% make sure that the last argument of \cs{@@_parse_infix:NN} is
+% defining |\__fp_exp_after_|\meta{type}|_f:nw|. In all cases, we
+% make sure that the second argument of \cs{@@_parse_infix:NN} is
% correctly expanded.
% \begin{macrocode}
-\cs_new:Npn \@@_parse_operand_relax:NN #1#2
+\cs_new:Npn \@@_parse_one_fp:NN #1#2
{
- \cs:w @@_parse_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end:
+ \cs:w @@_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end:
{
\exp_after:wN \@@_parse_infix:NN
\exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w
}
#2
}
-\cs_new_eq:NN \@@_parse_exp_after_f:nw \@@_exp_after_f:nw
-\cs_new:Npn \@@_parse_exp_after_mark_f:nw #1
+\cs_new:Npn \@@_exp_after_mark_f:nw #1
{
\__msg_kernel_expandable_error:nn { kernel } { fp-early-end }
- \exp_after:wN \c_nan_fp
- \tex_romannumeral:D -`0 #1
+ \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1
}
-\cs_new:cpn { @@_parse_exp_after_?_f:nw } #1#2
+\cs_new:cpn { @@_exp_after_?_f:nw } #1#2
{
- \__msg_kernel_expandable_error:nnn
- { kernel } { bad-variable } {#2}
- \exp_after:wN \c_nan_fp
- \tex_romannumeral:D -`0 #1
+ \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#2}
+ \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1
}
% \end{macrocode}
% \end{macro}
%
-% ^^A begin[todo]
+% \begin{macro}[aux, EXP]
+% {
+% \@@_parse_one_register:NN,
+% \@@_parse_one_register_aux:Nw,
+% \@@_parse_one_register_auxii:wwwNw,
+% \@@_parse_one_register_int:www,
+% \@@_parse_one_register_mu:www,
+% \@@_parse_one_register_dim:ww
+% }
+% This is called whenever~|#2| is a control sequence other than
+% \cs{scan_stop:} in meaning. We assume that it is a register, but
+% carefully unpacking it with \cs{tex_the:D} within braces. First, we
+% find the exponent following~|#2|. Then we unpack~|#2| with
+% \cs{tex_the:D}, and the \texttt{auxii} auxiliary distinguishes
+% integer registers from dimensions/skips from muskips, according to
+% the presence of a period and/or of |pt|. For integers, simply
+% convert \meta{value}|e|\meta{exponent} to a floating point number
+% with \cs{fp_parse:n} (this is somewhat wasteful). For other
+% registers, the decimal rounding provided by \TeX{} does not
+% accurately represent the binary value that it manipulates, so we
+% extract this binary value as a number of scaled points with
+% \cs{__int_value:w} \cs{__dim_eval:w} \meta{decimal value} |pt|, and
+% use an auxiliary of \cs{dim_to_fp:n}, which performs the
+% multiplication by $2^{-16}$, correctly rounded.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_register:NN #1#2
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \tex_romannumeral:D -`0
+ \exp_after:wN \@@_parse_one_register_aux:Nw
+ \exp_after:wN #2
+ \__int_value:w
+ \exp_after:wN \@@_parse_exponent:N
+ \tex_romannumeral:D \@@_parse_expand:w
+ }
+\group_begin:
+\char_set_catcode_other:N \P
+\char_set_catcode_other:N \T
+\char_set_catcode_other:N \M
+\char_set_catcode_other:N \U
+\tl_to_lowercase:n
+ {
+ \group_end:
+ \cs_new:Npn \@@_parse_one_register_aux:Nw #1
+ {
+ \exp_after:wN \use:nn
+ \exp_after:wN \@@_parse_one_register_auxii:wwwNw
+ \exp_after:wN { \tex_the:D \exp_not:N #1 }
+ ; \@@_parse_one_register_dim:ww
+ PT ; \@@_parse_one_register_mu:www
+ . PT ; \@@_parse_one_register_int:www
+ \q_stop
+ }
+ \cs_new:Npn \@@_parse_one_register_auxii:wwwNw
+ #1 . #2 PT #3 ; #4#5 \q_stop { #4 #1.#2; }
+ \cs_new:Npn \@@_parse_one_register_mu:www #1 MU; #2;
+ { \@@_parse_one_register_dim:ww #1; }
+ }
+\cs_new:Npn \@@_parse_one_register_int:www #1; #2.; #3;
+ { \@@_parse:n { #1 e #3 } }
+\cs_new:Npn \@@_parse_one_register_dim:ww #1; #2;
+ {
+ \exp_after:wN \@@_from_dim_test:ww
+ \__int_value:w #2 \exp_after:wN ,
+ \__int_value:w \__dim_eval:w #1 pt ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_one_digit:NN}
+% A digit marks the beginning of an explicit floating point number.
+% Once the number is found, we will catch the case of overflow and
+% underflow with \cs{@@_sanitize:wN}, then
+% \cs{@@_parse_infix_after_operand:NwN} expands \cs{@@_parse_infix:NN}
+% after the number we find, to wrap the following infix operator as
+% required. Finding the number itself begins by removing leading
+% zeros: further steps are described later.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_one_digit:NN #1
+ {
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
+ \exp_after:wN #1
+ \tex_romannumeral:D -`0
+ \exp_after:wN \@@_sanitize:wN
+ \int_use:N \__int_eval:w \c_zero \@@_parse_trim_zeros:N
+ }
+% \end{macrocode}
+% \end{macro}
%
-% \begin{macro}[aux, EXP]{\@@_parse_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{macro}[aux, EXP]{\@@_parse_one_other:NN}
+% For this function, |#2|~is a character token which is not a digit.
+% If it is a letter, \cs{@@_parse_letters:N} beyond this one and give
+% the result to \cs{@@_parse_word:Nw}. Otherwise, the character is
+% assumed to be a prefix operator, and we build
+% |\__fp_parse_prefix_|\meta{operator}|:Nw|.
% \begin{macrocode}
-\cs_new:Npn \@@_parse_operand_other:NN #1 #2
+\cs_new:Npn \@@_parse_one_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
+ \__int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - \c_thirty_two \fi: ) / 26
+ = \c_three
+ \exp_after:wN \@@_parse_word:Nw
\exp_after:wN #1
+ \exp_after:wN #2
+ \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N
\tex_romannumeral:D
- \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 \@@_parse_prefix:NNN
\exp_after:wN #1
\exp_after:wN #2
\cs:w @@_parse_prefix_#2:Nw \exp_after:wN \cs_end:
@@ -1033,161 +899,119 @@
\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;
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_word:Nw}
+% \begin{macro}[aux, rEXP]{\@@_parse_letters:N}
+% Finding letters is a simple recursion. Once \cs{@@_parse_letters:N}
+% has done its job, we try to build a control sequence from the
+% word~|#2|. If it is a known word, then the corresponding action is
+% taken, and otherwise, we complain about an unknown word, yield
+% \cs{c_nan_fp}, and look for the following infix operator. Note that
+% the unknown word could be a mistyped function as well as a mistyped
+% constant, so there is no way to tell whether to look for arguments;
+% we do not.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word:Nw #1#2;
{
\cs_if_exist_use:cF { @@_parse_word_#2:N }
{
\__msg_kernel_expandable_error:nnn
{ kernel } { unknown-fp-word } {#2}
- \exp_after:wN \c_nan_fp
- \tex_romannumeral:D -`0
- \@@_parse_infix:NN
+ \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
+\cs_new:Npn \@@_parse_letters:N #1
+ {
+ -`0
+ \if_int_compare:w
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \c_zero
+ \else:
+ \__int_eval:w
+ ( `#1 \if_int_compare:w `#1 > `Z - \c_thirty_two \fi: )
+ / 26
+ \fi:
+ = \c_three
+ \exp_after:wN #1
+ \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N
+ \tex_romannumeral:D
+ \else:
+ \@@_parse_return_semicolon:w #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
+% {\@@_parse_prefix:NNN, \@@_parse_prefix_unknown:NNN}
+% For this function, |#1|~is the previous \meta{precedence}, |#2|~is
+% the operator just seen, and |#3|~is a control sequence which
+% implements the operator if it is a known operator. If this control
+% sequence is \cs{scan_stop:}, then the operator is in fact unknown.
+% Either the expression is missing a number there (if the operator is
+% valid as an infix operator), and we put \texttt{nan}, wrapping the
+% infix operator in a csname as appropriate, or the character is
+% simply invalid in floating point expressions, and we continue
+% looking for a number, starting again from \cs{@@_parse_one:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_prefix:NNN #1#2#3
{
- \if_meaning:w \tex_relax:D #3
- \exp_after:wN \@@_parse_operand_other_prefix_unknown:NNN
+ \if_meaning:w \scan_stop: #3
+ \exp_after:wN \@@_parse_prefix_unknown:NNN
\exp_after:wN #2
\fi:
#3 #1
}
-\cs_new:Npn \@@_parse_operand_other_prefix_unknown:NNN #1#2#3
+\cs_new:Npn \@@_parse_prefix_unknown:NNN #1#2#3
{
\cs_if_exist:cTF { @@_parse_infix_#1:N }
{
\__msg_kernel_expandable_error:nnn
{ kernel } { fp-missing-number } {#1}
- \exp_after:wN \c_nan_fp
- \tex_romannumeral:D -`0
- \@@_parse_infix:NN #3 #1
+ \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0
+ \@@_parse_infix:NN #3 #1
}
{
\__msg_kernel_expandable_error:nnn
{ kernel } { fp-unknown-symbol } {#1}
- \@@_parse_operand:Nw #3
+ \@@_parse_one: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{Numbers: trimming leading zeros}
%
-% \subsubsection{Trimming leading zeros}
+% Numbers will be parsed as follows: first we trim leading zeros, then
+% if the next character is a digit, start reading a significand $\geq 1$
+% with the set of functions |\__fp_parse_large|\ldots{}; if it is a
+% period, the significand is~$<1$; and otherwise it is zero. In the
+% second case, trim additional zeros after the period, counting them for
+% an exponent shift $\meta{exp_1}<0$, then read the significand with the
+% set of functions |\__fp_parse_small|\ldots{} Once the significand is
+% read, read the exponent if |e|~is present.
%
% \begin{macro}[aux, rEXP]{\@@_parse_trim_zeros:N, \@@_parse_trim_end:w}
% This function expects an already expanded token. It removes any
-% leading zero, then 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
+% leading zero, then distinguishes three cases: if the first non-zero
+% token is a digit, then call \cs{@@_parse_large:N} (the significand
+% is $\geq 1$); if it is |.|, then continue trimming zeros with
% \cs{@@_parse_strim_zeros:N}; otherwise, our number is exactly zero,
% and we call \cs{@@_parse_zero:} to take care of that case.
% \begin{macrocode}
\cs_new:Npn \@@_parse_trim_zeros:N #1
{
- \if:w 0 #1
+ \if:w 0 \exp_not:N #1
\exp_after:wN \@@_parse_trim_zeros:N
\tex_romannumeral:D
\else:
- \if:w . #1
+ \if:w . \exp_not:N #1
\exp_after:wN \@@_parse_strim_zeros:N
\tex_romannumeral:D
\else:
@@ -1210,20 +1034,21 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux, rEXP]{\@@_parse_strim_zeros:N, \@@_parse_strim_end:w}
+% \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
+% 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.
+% smaller than~$1$), and otherwise, the number is an exact zero. The
+% name \texttt{strim} stands for \enquote{small trim}.
% \begin{macrocode}
\cs_new:Npn \@@_parse_strim_zeros:N #1
{
- \if:w 0 #1
+ \if:w 0 \exp_not:N #1
- \c_one
- \exp_after:wN \@@_parse_strim_zeros:N
- \tex_romannumeral:D
+ \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D
\else:
\@@_parse_strim_end:w #1
\fi:
@@ -1242,12 +1067,10 @@
% \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.
+% After reading a significand of~$0$, we need to remove any exponent,
+% then put a sign of~|1| for \cs{@@_sanitize:wN}, small hack to denote
+% an exact zero (rather than an underflow).
% \begin{macrocode}
\cs_new:Npn \@@_parse_zero:
{
@@ -1257,7 +1080,7 @@
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Small significand}
+% \subsubsection{Number: small significand}
%
% \begin{macro}[aux, rEXP]{\@@_parse_small:N}
% This function is called after we have passed the decimal separator
@@ -1268,8 +1091,8 @@
% 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
+% 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}
@@ -1291,12 +1114,12 @@
% \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
+% $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
+% 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
@@ -1323,12 +1146,13 @@
% \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
+% 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.
+% function expands to nothing, to |+\c_zero| or to |+\c_one|.
+% Otherwise, there is no $16$-th digit, so we put a~$0$, and look for
+% an exponent.
% \begin{macrocode}
\cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4
{
@@ -1354,18 +1178,18 @@
% }
% 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
+% 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
+% argument is~$2$. The \texttt{trailing} function has an exponent
% shift as its first argument, which we add to the exponent found in
% the |e...| syntax. If the trailing digits cause a carry, the
% integer expression for the leading digits is incremented (|+ \c_one|
% in the code below). If the leading digits propagate this carry all
% the way up, the function \cs{@@_parse_pack_carry:w} increments the
-% exponent, and changes the significand from |0000...| to |1000...|: this
-% is simple because such a carry can only occur to give rise to a
-% power of $10$.
+% exponent, and changes the significand from |0000...| to |1000...|:
+% this is simple because such a carry can only occur to give rise to a
+% power of~$10$.
% \begin{macrocode}
\cs_new:Npn \@@_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ;
{
@@ -1383,9 +1207,9 @@
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Large significand}
+% \subsubsection{Number: large significand}
%
-% Parsing a significand larger than $1$ is a little bit more difficult
+% 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
@@ -1395,8 +1219,8 @@
% \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.
+% 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
{
@@ -1412,15 +1236,15 @@
% \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
+% 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
+% 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
@@ -1434,7 +1258,7 @@
\exp_after:wN \@@_parse_digits_vi:N
\tex_romannumeral:D
\else:
- \if:w . #4
+ \if:w . \exp_not:N #4
\exp_after:wN \@@_parse_small_leading:wwNN
\__int_value:w 1
\cs:w
@@ -1460,19 +1284,19 @@
% \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$,
+% 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
+% 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$.
+% digit of~$0$.
% \begin{macrocode}
\cs_new:Npn \@@_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4
{
@@ -1487,7 +1311,7 @@
\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
+ \if:w . \exp_not:N #4
\exp_after:wN \@@_parse_small_trailing:wwNN
\__int_value:w 1
\cs:w
@@ -1505,7 +1329,167 @@
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Finding the exponent}
+% \subsubsection{Number: beyond 16 digits, rounding}
+%
+% \begin{macro}[aux, rEXP]{\@@_parse_round_loop:N, \@@_parse_round_up:N}
+% This loop is called when rounding a number (whether the mantissa is
+% small or large). It should appear in an integer expression. This
+% function reads digits one by one, until reaching a non-digit, and
+% adds~$1$ to the integer expression for each digit. If all digits
+% found are~$0$, the function ends the expression by |;\c_zero|,
+% otherwise by |;\c_one|. This is done by switching the loop to
+% |round_up| at the first non-zero digit, thus we avoid to test
+% whether digits are~$0$ or not once we see a first non-zero digit.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_loop:N #1
+ {
+ \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+ + \c_one
+ \if:w 0 \token_to_str:N #1
+ \exp_after:wN \@@_parse_round_loop:N
+ \tex_romannumeral:D
+ \else:
+ \exp_after:wN \@@_parse_round_up:N
+ \tex_romannumeral:D
+ \fi:
+ \else:
+ \@@_parse_return_semicolon:w \c_zero #1
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_round_up:N #1
+ {
+ \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f:
+ + \c_one
+ \exp_after:wN \@@_parse_round_up:N
+ \tex_romannumeral:D
+ \else:
+ \@@_parse_return_semicolon:w \c_one #1
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, rEXP]{\@@_parse_round_after:wN}
+% After the loop \cs{@@_parse_round_loop:N}, this function fetches an
+% exponent with \cs{@@_parse_exponent:N}, and combines it with the
+% number of digits counted by \cs{@@_parse_round_loop:N}. At the same
+% time, the result \cs{c_zero} or \cs{c_one} is added to the
+% surrounding integer expression.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_round_after:wN #1; #2
+ {
+ + #2 \exp_after:wN ;
+ \int_use:N \__int_eval:w #1 + \@@_parse_exponent:N
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, rEXP]
+% {\@@_parse_small_round:NN, \@@_parse_round_after:wN}
+% Here, |#1|~is the digit that we are currently rounding (we only care
+% whether it is even or odd). If |#2|~is not a digit, then fetch an
+% exponent and expand to |;|\meta{exponent} only. Otherwise, we will
+% expand to |+\c_zero| or |+\c_one|, then |;|\meta{exponent}. To
+% decide which, call \cs{@@_round_s:NNNw} to know whether to round up,
+% giving it as arguments a sign~$0$ (all explicit numbers are
+% positive), the digit |#1|~to round, the first following digit~|#2|,
+% and either |+\c_zero| or |+\c_one| depending on whether the
+% following digits are all zero or not. This last argument is
+% obtained by \cs{@@_parse_round_loop:N}, whose number of digits we
+% discard by multiplying it by~$0$. The exponent which follows the
+% number is also fetched by \cs{@@_parse_round_after:wN}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_small_round:NN #1#2
+ {
+ \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_use:N \__int_eval:w
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_use:N \__int_eval:w \c_zero * \__int_eval:w \c_zero
+ \exp_after:wN \@@_parse_round_loop:N
+ \tex_romannumeral:D
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[aux, rEXP]
+% {
+% \@@_parse_large_round:NN,
+% \@@_parse_large_round_test:NN,
+% \@@_parse_large_round_aux:wNN,
+% }
+% Large numbers are harder to round, as there may be a period in the
+% way. Again, |#1|~is the digit that we are currently rounding (we
+% only care whether it is even or odd). If there are no more digits
+% (|#2|~is not a digit), then we must test for a period: if there is
+% one, then switch to the rounding function for small significands,
+% otherwise fetch an exponent. If there are more digits (|#2|~is a
+% digit), then round, checking with \cs{@@_parse_round_loop:N} if all
+% further digits vanish, or some are non-zero. This loop is not
+% enough, as it is stopped by a period. After the loop, the
+% \texttt{aux} function tests for a period: if it is present, then we
+% must continue looking for digits, this time discarding the number of
+% digits we find.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_large_round:NN #1#2
+ {
+ \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f:
+ +
+ \exp_after:wN \@@_round_s:NNNw
+ \exp_after:wN 0
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \int_use:N \__int_eval:w
+ \exp_after:wN \@@_parse_large_round_aux:wNN
+ \int_use:N \__int_eval:w \c_one
+ \exp_after:wN \@@_parse_round_loop:N
+ \else: %^^A could be dot, or e, or other
+ \exp_after:wN \@@_parse_large_round_test:NN
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \fi:
+ }
+\cs_new:Npn \@@_parse_large_round_test:NN #1#2
+ {
+ \if:w . \exp_not:N #2
+ \exp_after:wN \@@_parse_small_round:NN
+ \exp_after:wN #1
+ \tex_romannumeral:D
+ \else:
+ \@@_parse_exponent:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_large_round_aux:wNN #1 ; #2 #3
+ {
+ + #2
+ \exp_after:wN \@@_parse_round_after:wN
+ \int_use:N \__int_eval:w #1
+ \if:w . \exp_not:N #3
+ + \c_zero * \__int_eval:w \c_zero
+ \exp_after:wN \@@_parse_round_loop:N
+ \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN ;
+ \exp_after:wN \c_zero
+ \exp_after:wN #3
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Number: finding the exponent}
%
% Expansion is a little bit tricky here, in part because we accept input
% where multiplication is implicit.
@@ -1532,12 +1516,12 @@
% 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.
+% token.
%
% \begin{macro}[aux, rEXP]{\@@_parse_exponent:Nw}
% This auxiliary is convenient to smuggle some material through
% \cs{fi:} ending conditional processing. We place those \cs{fi:}
-% (argument |#2|) at a very odd place because this allows us to insert
+% (argument~|#2|) at a very odd place because this allows us to insert
% \cs{__int_eval:w} \ldots{} there if needed.
% \begin{macrocode}
\cs_new:Npn \@@_parse_exponent:Nw #1 #2 \@@_parse_expand:w
@@ -1548,20 +1532,21 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[aux, rEXP]{\@@_parse_exponent:N, \@@_parse_exponent_aux:N}
-% This function should be called within an \cs{__int_value:w} expansion
-% (or within an integer expression. It leaves digits of the exponent
-% behind it in the input stream, and terminates the expansion with a
-% semicolon. If there is no \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{macro}[aux, rEXP]
+% {\@@_parse_exponent:N, \@@_parse_exponent_aux:N}
+% This function should be called within an \cs{__int_value:w}
+% expansion (or within an integer expression. It leaves digits of the
+% exponent behind it in the input stream, and terminates the expansion
+% with a semicolon. If there is no~|e|, leave an exponent of~$0$. If
+% there is an~|e|, expand the next token to run some tests on it. The
+% first rough test is that if the character code of~|#1| is greater
+% than that of~|9| (largest code valid for an exponent, less than any
+% code valid for an identifier), there was in fact no exponent;
+% otherwise, we search for the sign of the exponent.
% \begin{macrocode}
\cs_new:Npn \@@_parse_exponent:N #1
{
- \if:w e #1
+ \if:w e \exp_not:N #1
\exp_after:wN \@@_parse_exponent_aux:N
\tex_romannumeral:D
\else:
@@ -1571,7 +1556,7 @@
}
\cs_new:Npn \@@_parse_exponent_aux:N #1
{
- \if_int_compare:w \if_catcode:w \tex_relax:D #1
+ \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #1
\c_zero \else: `#1 \fi: > `9 \exp_stop_f:
0 \exp_after:wN ; \exp_after:wN e
\else:
@@ -1587,7 +1572,7 @@
% \begin{macrocode}
\cs_new:Npn \@@_parse_exponent_sign:N #1
{
- \if:w + \if:w - #1 + \fi: \token_to_str:N #1
+ \if:w + \if:w - \exp_not:N #1 + \fi: \token_to_str:N #1
\exp_after:wN \@@_parse_exponent_sign:N
\tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
\else:
@@ -1624,8 +1609,8 @@
% \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
+% we do not check for overflow of the exponent, hence there can be a
+% \TeX{} error. It is mostly harmless, except when parsing
% |0e9876543210|, which should be a valid representation of $0$, but
% is not.
% \begin{macrocode}
@@ -1644,9 +1629,9 @@
% \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:
+% 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;
@@ -1659,9 +1644,10 @@
% \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
+ \if_catcode:w \scan_stop: \exp_not:N #1
+ \if_meaning:w \scan_stop: #1
+ \if_int_compare:w
+ \pdftex_strcmp:D { \s_@@ } { \exp_not:N #1 } = \c_zero
0
\__msg_kernel_expandable_error:nnn
{ kernel } { fp-after-e } { floating~point~ }
@@ -1694,363 +1680,138 @@
% \end{macrocode}
% \end{macro}
%
-% ^^A begin[todo]
-% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead"
-%
-% \subsubsection{Beyond 16 digits: rounding}
+% \subsection{Constants, functions and prefix operators}
%
-% \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.
+% \subsubsection{Prefix operators}
%
+% \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw}
+% A unary~|+| does nothing: we should continue looking for a number.
% \begin{macrocode}
-\cs_new: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_aux: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_aux:wN #1 ; #2
- {
- + #2
- \exp_after:wN ;
- \int_use:N \__int_eval:w #1 +
- \@@_parse_exponent:N
- }
+\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_one:Nw
% \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{macro}[aux, EXP]{\@@_parse_apply_unary:NNNwN}
+% Here, |#1| is a precedence, |#2| is some extra data used by some
+% functions, |#3| is \emph{e.g.}, \cs{@@_sin_o:w}, and expands once
+% after the calculation, |#4| is the operand, and |#5| is a
+% |\__fp_parse_infix_...:N| function. We feed the data~|#2|, and the
+% argument~|#4|, to the function~|#3|, which expands
+% \cs{tex_romannumeral:D} thus the \texttt{infix} function~|#5|.
% \begin{macrocode}
-\cs_new:Npn \@@_parse_small_round:NN #1#2
+\cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5
{
- \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
+ #3 #2 #4 @
+ \tex_romannumeral:D -`0 #5 #1
}
% \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{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
+% The unary~|-| and boolean not are harder: we parse the operand using
+% a precedence equal to the maximum of the previous precedence~|##1|
+% and the precedence \cs{c_twelve} of the unary operator, then call
+% the appropriate |\__fp_|\meta{operation}|_o:w| function,
+% where the \meta{operation} is |set_sign| or |not|.
% \begin{macrocode}
-\cs_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
+\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
{
-%<assert> \assert_str_eq:nn { #2 } { \@@_parse_infix_end:N \s_@@_mark }
- \c_zero #1
+ \cs_new:cpn { @@_parse_prefix_ #1 :Nw } ##1
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN ##1
+ \exp_after:wN #4
+ \cs:w @@_#3_o:w \exp_after:wN \cs_end:
+ \tex_romannumeral:D
+ \if_int_compare:w #2 < ##1
+ \@@_parse_operand:Nw ##1
+ \else:
+ \@@_parse_operand:Nw #2
+ \fi:
+ \@@_parse_expand:w
+ }
}
+\@@_tmp:w - \c_twelve { set_sign } 2
+\@@_tmp:w ! \c_twelve { not } ?
% \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{macro}[EXP, aux]{\@@_parse_prefix_.:Nw}
+% Numbers which start with a decimal separator (a~period) end up here.
+% Of course, we do not look for an operand, but for the rest of the
+% number. This function is very similar to \cs{@@_parse_one_digit:NN}
+% but calls \cs{@@_parse_strim_zeros:N} to trim zeros after the
+% decimal point, rather than the \texttt{trim_zeros} function for
+% zeros before the decimal point.
% \begin{macrocode}
-\cs_new:Npn \@@_parse_until:Nw #1
+\cs_new:cpn { @@_parse_prefix_.:Nw } #1
{
- -`0
- \exp_after:wN \@@_parse_until_test:NwN
+ \exp_after:wN \@@_parse_infix_after_operand:NwN
\exp_after:wN #1
\tex_romannumeral:D -`0
- \exp_after:wN \@@_parse_operand:Nw
- \exp_after:wN #1
- \tex_romannumeral:D
+ \exp_after:wN \@@_sanitize:wN
+ \int_use:N \__int_eval:w \c_zero \@@_parse_strim_zeros:N
}
-\cs_new:Npn \@@_parse_until_test:NwN #1 #2 @ #3 { #3 #1 #2 @ }
-\cs_new_eq:NN \@@_parse_stop_until:N \use_none:n
-% \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{macro}[aux, EXP]
+% {\@@_parse_prefix_(:Nw, \@@_parse_lparen_after:NwN}
+% The left parenthesis is treated as a unary prefix operator because
+% it appears in exactly the same settings. Commas will be allowed if
+% the previous precedence is $16$ (function with multiple arguments)
+% or $13$ (unary boolean \enquote{not}). In this case, find an
+% operand using the precedence~$1$; otherwise the precedence~$0$.
+% Once the operand is found, the \texttt{lparen_after} auxiliary makes
+% sure that there was a closing parenthesis (otherwise it complains),
+% and leaves in the input stream the array it found as an operand,
+% fetching the following infix operator.
% \begin{macrocode}
-\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
+ \char_set_catcode_letter:N (
+ \char_set_catcode_letter:N )
+ \cs_new:Npn \@@_parse_prefix_(:Nw #1
{
- \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:
+ \exp_after:wN \@@_parse_lparen_after:NwN
+ \exp_after:wN #1
+ \tex_romannumeral:D
+ \if_int_compare:w #1 = \c_sixteen
+ \@@_parse_operand:Nw \c_one
\else:
- \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:
+ \@@_parse_operand:Nw \c_zero
\fi:
- #1
- #2
+ \@@_parse_expand:w
}
- \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3
+ \cs_new:Npn \@@_parse_lparen_after:NwN #1#2 @ #3
{
- \if_meaning:w \tex_relax:D #1
- \__msg_kernel_expandable_error:nnn { kernel } { fp-missing } { * }
- \exp_after:wN \@@_parse_infix_*:N
- \exp_after:wN #2
- \exp_after:wN #3
- \else:
- \exp_after:wN #1
- \exp_after:wN #2
- \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
- \fi:
+ \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N
+ {
+ \@@_exp_after_array_f:w #2 \s_@@_stop
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN #1
+ \tex_romannumeral:D \@@_parse_expand:w
+ }
+ {
+ \__msg_kernel_expandable_error:nnn
+ { kernel } { fp-missing } { ) }
+ #2 @ \use_none:n #3
+ }
}
\group_end:
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int, EXP]{\@@_parse_apply_binary:NwNwN}
-% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation}
-% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate
-% call to the \meta{operation} |#4|, given the types of the two
-% \meta{operands}.
-% \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
- _ #4
- \@@_type_from_scan:N #5
- _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{@@_sin_o: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 _o:w \cs_end: #1 }
-% \end{macrocode}
-% \end{macro}
-%
-% \subsection{Prefix operators}
-%
-% \subsubsection{Identifiers}
+% \subsubsection{Constants}
%
% \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_inf:N , \@@_parse_word_nan:N ,
+% \@@_parse_word_pi:N , \@@_parse_word_deg:N ,
% \@@_parse_word_true:N , \@@_parse_word_false:N ,
% }
-% A whole bunch of floating point numbers.
+% Some words correspond to constant floating points. The floating
+% point constant is left as a result of \cs{@@_parse_one:Nw} after
+% expanding \cs{@@_parse_infix:NN}.
% \begin{macrocode}
\cs_set_protected:Npn \@@_tmp:w #1 #2
{
@@ -2063,7 +1824,20 @@
\@@_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
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
+% {
+% \@@_parse_word_pt:N , \@@_parse_word_in:N ,
+% \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N ,
+% \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N ,
+% \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N ,
+% }
+% Dimension units are also floating point constants but their value is
+% not stored as a floating point constant. We give the values
+% explicitly here.
+% \begin{macrocode}
\cs_set_protected:Npn \@@_tmp:w #1 #2
{
\cs_new_nopar:cpn { @@_parse_word_#1:N }
@@ -2072,6 +1846,7 @@
\s_@@ \@@_chk:w 10 #2 ;
}
}
+\@@_tmp:w {pt} { {1} {1000} {0000} {0000} {0000} }
\@@_tmp:w {in} { {2} {7227} {0000} {0000} {0000} }
\@@_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} }
\@@_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} }
@@ -2082,73 +1857,140 @@
\@@_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} }
\@@_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} }
\@@_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_word_em:N, \@@_parse_word_ex:N}
+% The font-dependent units |em| and |ex| must be evaluated on the fly.
+% We reuse an auxiliary of \cs{dim_to_fp:n}.
+% \begin{macrocode}
\tl_map_inline:nn { {em} {ex} }
{
\cs_new_nopar:cpn { @@_parse_word_#1:N }
{
- \exp_after:wN \dim_to_fp:n \exp_after:wN
- { \dim_use:N \__dim_eval:w 1 #1 \exp_after:wN }
+ \exp_after:wN \@@_from_dim_test:ww
+ \exp_after:wN 0 \exp_after:wN ,
+ \__int_value:w \__dim_eval:w 1 #1 \exp_after:wN ;
\tex_romannumeral:D -`0 \@@_parse_infix:NN
}
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int, EXP]
+% \subsubsection{Functions}
+%
+% ^^A begin[todo]
+%
+% ^^A todo: test <15 digits>1500000000.1
+% ^^A todo: test <15 digits>1517263572.000
+%
+% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead"
+%
+% \begin{macro}[aux, EXP]
+% {\@@_parse_unary_function:nNN, \@@_parse_function:NNN}
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_unary_function:nNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \cs:w @@_#1_o:w \exp_after:wN \cs_end:
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_fifteen \@@_parse_expand:w
+ }
+\cs_new:Npn \@@_parse_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_parse_apply_unary:NNNwN
+ \exp_after:wN #3
+ \exp_after:wN #2
+ \exp_after:wN #1
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
+% {
+% \@@_parse_word_acot:N , \@@_parse_word_acotd:N,
+% \@@_parse_word_atan:N , \@@_parse_word_atand:N,
+% \@@_parse_word_max:N , \@@_parse_word_min:N ,
+% }
+% Those functions are also unary (not binary), but may receive a
+% variable number of arguments.
+% \begin{macrocode}
+\cs_new_nopar:Npn \@@_parse_word_acot:N
+ { \@@_parse_function:NNN \@@_acot_o:Nw \use_i:nn }
+\cs_new_nopar:Npn \@@_parse_word_acotd:N
+ { \@@_parse_function:NNN \@@_acot_o:Nw \use_ii:nn }
+\cs_new_nopar:Npn \@@_parse_word_atan:N
+ { \@@_parse_function:NNN \@@_atan_o:Nw \use_i:nn }
+\cs_new_nopar:Npn \@@_parse_word_atand:N
+ { \@@_parse_function:NNN \@@_atan_o:Nw \use_ii:nn }
+\cs_new_nopar:Npn \@@_parse_word_max:N
+ { \@@_parse_function:NNN \@@_minmax_o:Nw 2 }
+\cs_new_nopar:Npn \@@_parse_word_min:N
+ { \@@_parse_function:NNN \@@_minmax_o:Nw 0 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
% {
% \@@_parse_word_abs:N ,
+% \@@_parse_word_exp:N ,
+% \@@_parse_word_ln:N ,
+% }
+% Unary functions.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_word_abs:N
+ { \@@_parse_unary_function:nNN { set_sign } 0 }
+\cs_new_nopar:Npn \@@_parse_word_exp:N
+ { \@@_parse_unary_function:nNN {exp} ? }
+\cs_new_nopar:Npn \@@_parse_word_ln:N
+ { \@@_parse_unary_function:nNN {ln} ? }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]
+% {
+% \@@_parse_word_acos:N ,
+% \@@_parse_word_acosd:N ,
+% \@@_parse_word_acsc:N ,
+% \@@_parse_word_acscd:N ,
+% \@@_parse_word_asec:N ,
+% \@@_parse_word_asecd:N ,
+% \@@_parse_word_asin:N ,
+% \@@_parse_word_asind:N ,
% \@@_parse_word_cos:N ,
+% \@@_parse_word_cosd:N ,
% \@@_parse_word_cot:N ,
+% \@@_parse_word_cotd:N ,
% \@@_parse_word_csc:N ,
-% \@@_parse_word_exp:N ,
-% \@@_parse_word_ln:N ,
+% \@@_parse_word_cscd:N ,
% \@@_parse_word_sec:N ,
+% \@@_parse_word_secd:N ,
% \@@_parse_word_sin:N ,
+% \@@_parse_word_sind:N ,
% \@@_parse_word_tan:N ,
+% \@@_parse_word_tand:N ,
% }
-% Unary functions, which are applied to all of their arguments when
-% receiving an array.
+% Unary functions.
% \begin{macrocode}
\tl_map_inline:nn
- { {abs} {cos} {cot} {csc} {exp} {ln} {sec} {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
- }
+ {acos} {acsc} {asec} {asin}
+ {cos} {cot} {csc} {sec} {sin} {tan}
}
-% \end{macrocode}
-% \end{macro}
-%
-% \begin{macro}[int, EXP]
-% {
-% \@@_parse_word_max:N , \@@_parse_word_min: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
- }
+ \cs_new_nopar:cpn { @@_parse_word_#1:N }
+ { \@@_parse_unary_function:nNN {#1} \use_i:nn }
+ \cs_new_nopar:cpn { @@_parse_word_#1d:N }
+ { \@@_parse_unary_function:nNN {#1} \use_ii:nn }
}
-\@@_tmp:w \@@_parse_word_max:N \@@_max_o:w
-\@@_tmp:w \@@_parse_word_min:N \@@_min_o:w
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int, EXP]{\@@_parse_word_round:N}
+% \begin{macro}[aux, EXP]{\@@_parse_word_round:N}
% This function expects one or two arguments.
% \begin{macrocode}
\cs_new:Npn \@@_parse_word_round:N #1#2
@@ -2168,7 +2010,7 @@
\exp_after:wN #1
\exp_after:wN \@@_round_to_nearest:NNN
\tex_romannumeral:D
- \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w #2
+ \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w #2
}
\cs_new:Npn \@@_parse_round:Nw
#1 #2 \@@_round_to_nearest:NNN #3 \@@_parse_expand:w #4
@@ -2188,151 +2030,308 @@
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Unary minus, plus, not}
+% \subsection{Main functions}
%
-% \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw}
-% A unary |+| does nothing.
+% \begin{macro}[int, EXP]{\@@_parse:n}
+% \begin{macro}[aux, EXP]{\@@_parse_after:ww}
+% Start a \tn{romannumeral} expansion so that \cs{@@_parse:n} expands
+% in two steps. The \cs{@@_parse_operand:Nw} function will perform
+% computations until reaching an operation with precedence
+% \cs{c_minus_one} or less. Then stop the initial expansion with
+% \cs{c_zero}.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse:n #1
+ {
+ \tex_romannumeral:D
+ \exp_after:wN \@@_parse_after:ww
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_minus_one
+ \@@_parse_expand:w #1 \s_@@_mark
+ \s_@@_stop
+ }
+\cs_new:Npn \@@_parse_after:ww
+ #1@ \@@_parse_infix_end:N \s_@@_mark \s_@@_stop
+ { \c_zero #1 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_operand:Nw}
+% \begin{macro}[aux, EXP]{\@@_parse_continue:NwN}
+% The \cs{@@_parse_operand}
+% This is just a shorthand which sets up both \cs{@@_parse_continue}
+% and \cs{@@_parse_one} with the same precedence. Note the
+% trailing \cs{tex_romannumeral:D}. This function should be
+% used with much care.
% \begin{macrocode}
-\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_operand:Nw
+\cs_new:Npn \@@_parse_operand:Nw #1
+ {
+ -`0
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \tex_romannumeral:D -`0
+ \exp_after:wN \@@_parse_one:Nw
+ \exp_after:wN #1
+ \tex_romannumeral:D
+ }
+\cs_new:Npn \@@_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ }
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-% \begin{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw}
-% Unary |-| is harder.
-% Boolean not.
+% \begin{macro}[aux, EXP]{\@@_parse_apply_binary:NwNwN}
+% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation}
+% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate
+% call to the \meta{operation}~|#3|.
% \begin{macrocode}
-\cs_set_protected:Npn \@@_tmp:w #1#2
+\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2@ #3 #4@ #5
{
- \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
- }
+ \exp_after:wN \@@_parse_continue:NwN
+ \exp_after:wN #1
+ \tex_romannumeral:D -`0 \cs:w @@_#3_o:ww \cs_end: #2 #4
+ \tex_romannumeral:D -`0 #5 #1
}
-\@@_tmp:w - { - }
-\@@_tmp:w ! { ! }
% \end{macrocode}
% \end{macro}
%
-% \subsubsection{Other prefixes}
+% \subsection{Infix operators}
%
-% \begin{macro}[int]{\@@_parse_prefix_(:Nw}
+% \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:cpn { @@_parse_prefix_(:Nw } #1
+ \char_set_catcode_letter:N \*
+ \cs_new:Npn \@@_parse_infix:NN #1 #2
{
- \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
+ \if_catcode:w \scan_stop: \exp_not:N #2
+ \if_int_compare:w
+ \pdftex_strcmp:D { \s_@@_mark } { \exp_not:N #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:
- \@@_parse_until:Nw \c_zero
+ \if_int_compare:w
+ \__int_eval:w
+ ( `#2 \if_int_compare:w `#2 > `Z - \c_thirty_two \fi: )
+ / 26
+ = \c_three
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \@@_parse_infix_juxtapose:N
+ \else:
+ \exp_after:wN \@@_parse_infix_check:NNN
+ \cs:w
+ @@_parse_infix_#2:N
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \cs_end:
+ \fi:
\fi:
- \@@_parse_expand:w
+ #1
+ #2
}
- \cs_new:Npn \@@_parse_lparen_after:NwN #1#2@#3
+ \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3
{
- \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N
- {
- \@@_exp_after_array_f:w #2 \s_@@_stop
- \exp_after:wN \@@_parse_infix:NN
- \exp_after:wN #1
- \tex_romannumeral:D \@@_parse_expand:w
- }
- {
- \__msg_kernel_expandable_error:nnn { kernel } { fp-missing } { ) }
- #2 @ \@@_parse_stop_until:N #3
- }
+ \if_meaning:w \scan_stop: #1
+ \__msg_kernel_expandable_error:nnn
+ { kernel } { fp-missing } { * }
+ \exp_after:wN \@@_parse_infix_*:N
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \else:
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
+ \fi:
}
\group_end:
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\@@_parse_prefix_.:Nw}
-% This function is called when a number starts with a dot.
+% \subsubsection{Closing parentheses and commas}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_infix_end:N}
+% This one is a little bit odd: force every previous operator to end,
+% regardless of the precedence.
% \begin{macrocode}
-\cs_new: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
- }
+\cs_new:Npn \@@_parse_infix_end:N #1
+ { @ \use_none:n \@@_parse_infix_end:N }
% \end{macrocode}
% \end{macro}
%
-% \subsection{Infix operators}
+% \begin{macro}[aux, EXP]+\@@_parse_infix_):N+
+% This is very similar to \cs{@@_parse_infix_end:N}, complaining about
+% an extra closing parenthesis if the previous operator was the
+% beginning of the expression.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_letter:N \)
+ \cs_new:Npn \@@_parse_infix_):N #1
+ {
+ \if_int_compare:w #1 < \c_zero
+ \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) }
+ \exp_after:wN \@@_parse_infix:NN
+ \exp_after:wN #1
+ \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_infix_):N
+ \fi:
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
%
-% 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{macro}[aux, EXP]+\@@_parse_infix_,:N+
% \begin{macrocode}
-\cs_set_protected:Npn \@@_tmp:w #1#2#3#4
- {
- \cs_new:Npn #1 ##1
- {
- \if_int_compare:w ##1 < #3
- \exp_after:wN @
- \exp_after:wN \@@_parse_apply_binary:NwNwN
- \exp_after:wN #2
+\group_begin:
+ \char_set_catcode_letter:N \,
+ \cs_new:Npn \@@_parse_infix_,:N #1
+ {
+ \if_int_compare:w #1 > \c_one
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_infix_,:N
+ \else:
+ \if_int_compare:w #1 = \c_one
+ \exp_after:wN \@@_parse_infix_comma:w
\tex_romannumeral:D
- \@@_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
+ \exp_after:wN \@@_parse_infix_comma_gobble:w
+ \tex_romannumeral:D
\fi:
- }
- }
+ \@@_parse_operand:Nw \c_one
+ \exp_after:wN \@@_parse_expand:w
+ \fi:
+ }
+ \cs_new:Npn \@@_parse_infix_comma:w #1 @
+ { #1 @ \use_none:n }
+ \cs_new:Npn \@@_parse_infix_comma_gobble:w #1 @
+ {
+ \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma }
+ @ \use_none:n
+ }
+\group_end:
% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Usual infix operators}
%
-% \begin{macro}[int, EXP]
+% \begin{macro}[aux, EXP]
% {
% \@@_parse_infix_+:N, \@@_parse_infix_-:N,
% \@@_parse_infix_/:N, \@@_parse_infix_mul:N,
% \@@_parse_infix_and:N, \@@_parse_infix_or:N,
% }
-% Using the general mechanism for arithmetic operations.
+% \begin{macro}[aux, EXP]+\@@_parse_infix_^:N+ As described in the
+% \enquote{work plan}, each infix operator has an associated
+% \cs{infix} function, a computing function, and precedence, given as
+% arguments to \cs{@@_tmp:w}. Using the general mechanism for
+% arithmetic operations. The power operation must be associative in
+% the opposite order from all others. For this, we use two distinct
+% precedences.
% \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
+ \char_set_catcode_letter:N \^
+ \char_set_catcode_letter:N \/
+ \char_set_catcode_letter:N \-
+ \char_set_catcode_letter:N \+
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3#4
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \if_int_compare:w ##1 < #3
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_binary:NwNwN
+ \exp_after:wN #2
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw #4
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN #1
+ \fi:
+ }
+ }
+ \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen
+ \@@_tmp:w \@@_parse_infix_/:N / \c_ten \c_ten
+ \@@_tmp:w \@@_parse_infix_mul:N * \c_ten \c_ten
+ \@@_tmp:w \@@_parse_infix_-:N - \c_nine \c_nine
+ \@@_tmp:w \@@_parse_infix_+:N + \c_nine \c_nine
+ \@@_tmp:w \@@_parse_infix_and:N & \c_five \c_five
+ \@@_tmp:w \@@_parse_infix_or:N | \c_four \c_four
\group_end:
% \end{macrocode}
% \end{macro}
+% \end{macro}
+%
+% \subsubsection{Juxtaposition}
+%
+% \begin{macro}[aux, EXP]+\@@_parse_infix_(:N+
+% When an opening parenthesis appears where we expect an infix
+% operator, we compute the product of the previous operand and the
+% contents of the parentheses using \cs{@@_parse_infix_juxtapose:N}.
+% \begin{macrocode}
+\cs_new:cpn { @@_parse_infix_(:N } #1
+ { \@@_parse_infix_juxtapose:N #1 ( }
+% \end{macrocode}
+% \end{macro}
%
-% \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{macro}[aux, EXP]
+% {\@@_parse_infix_juxtapose:N, \@@_parse_apply_juxtapose:NwwN}
+% Juxtaposition follows the same scheme as other binary operations,
+% but calls \cs{@@_parse_apply_juxtapose:NwwN} rather than directly
+% calling \cs{@@_parse_apply_binary:NwNwN}. This lets us catch errors
+% such as |max(1,2,3)pt| where one operand of the juxtaposition is not
+% a single number: both |#3| and~|#5| of the \texttt{apply} auxiliary
+% must be empty.
+% \begin{macrocode}
+\cs_new:Npn \@@_parse_infix_juxtapose:N #1
+ {
+ \if_int_compare:w #1 < \c_thirty_two
+ \exp_after:wN @
+ \exp_after:wN \@@_parse_apply_juxtapose:NwwN
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_thirty_two
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_infix_juxtapose:N
+ \fi:
+ }
+\cs_new:Npn \@@_parse_apply_juxtapose:NwwN #1 #2;#3@ #4;#5@
+ {
+ \if_catcode:w ^ \tl_to_str:n { #3 #5 } ^
+ \else:
+ \@@_error:nffn { invalid-ii }
+ { \@@_array_to_clist:n { #2; #3 } }
+ { \@@_array_to_clist:n { #4; #5 } }
+ { }
+ \fi:
+ \@@_parse_apply_binary:NwNwN #1 #2;@ * #4;@
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Multi-character cases}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_infix_*:N}
% \begin{macrocode}
\group_begin:
\char_set_catcode_letter:N ^
- \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen
\cs_new:cpn { @@_parse_infix_*:N } #1#2
{
- \if:w * #2
+ \if:w * \exp_not:N #2
\exp_after:wN \@@_parse_infix_^:N
\exp_after:wN #1
\else:
@@ -2344,17 +2343,16 @@
\group_end:
% \end{macrocode}
% \end{macro}
-% \end{macro}
%
-% \begin{macro}[int, EXP]+\@@_parse_infix_|:Nw+
-% \begin{macro}[int, EXP]+\@@_parse_infix_&:Nw+
+% \begin{macro}[aux, EXP]+\@@_parse_infix_|:Nw+
+% \begin{macro}[aux, EXP]+\@@_parse_infix_&:Nw+
% \begin{macrocode}
\group_begin:
\char_set_catcode_letter:N \|
\char_set_catcode_letter:N \&
\cs_new:Npn \@@_parse_infix_|:N #1#2
{
- \if:w | #2
+ \if:w | \exp_not:N #2
\exp_after:wN \@@_parse_infix_|:N
\exp_after:wN #1
\tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
@@ -2366,7 +2364,7 @@
}
\cs_new:Npn \@@_parse_infix_&:N #1#2
{
- \if:w & #2
+ \if:w & \exp_not:N #2
\exp_after:wN \@@_parse_infix_&:N
\exp_after:wN #1
\tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
@@ -2381,7 +2379,49 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}[int, EXP]
+% \subsubsection{Ternary operator}
+%
+% \begin{macro}[aux, EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N}
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_letter:N \?
+ \cs_new:Npn \@@_parse_infix_?:N #1
+ {
+ \if_int_compare:w #1 < \c_three
+ \exp_after:wN @
+ \exp_after:wN \@@_ternary:NwwN
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_three
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_infix_?:N
+ \fi:
+ }
+ \cs_new:Npn \@@_parse_infix_::N #1
+ {
+ \if_int_compare:w #1 < \c_three
+ \__msg_kernel_expandable_error:nnnn
+ { kernel } { fp-missing } { ? } { ~for~?: }
+ \exp_after:wN @
+ \exp_after:wN \@@_ternary_auxii:NwwN
+ \tex_romannumeral:D
+ \@@_parse_operand:Nw \c_two
+ \exp_after:wN \@@_parse_expand:w
+ \else:
+ \exp_after:wN @
+ \exp_after:wN \use_none:n
+ \exp_after:wN \@@_parse_infix_::N
+ \fi:
+ }
+\group_end:
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Comparisons}
+%
+% \begin{macro}[aux, EXP]
% {
% \@@_parse_infix_<:N, \@@_parse_infix_=:N,
% \@@_parse_infix_>:N, \@@_parse_infix_!:N
@@ -2434,14 +2474,14 @@
\exp_after:wN \@@_parse_infix_excl_error:
\else:
\exp_after:wN @
- \exp_after:wN \@@_parse_stop_until:N
+ \exp_after:wN \use_none:n
\exp_after:wN \@@_infix_compare:N
\fi:
}
\cs_new:Npn \@@_parse_compare:NNNNNNw #1#2#3#4#5#6#7
{
\if_case:w
- \if_catcode:w \tex_relax:D #7
+ \if_catcode:w \scan_stop: #7
\c_minus_one
\else:
\__int_eval:w `#7 - `< \__int_eval_end:
@@ -2474,11 +2514,11 @@
\exp_after:wN #3
\exp_after:wN #4
\tex_romannumeral:D
- \@@_parse_until:Nw \c_seven \@@_parse_expand:w #5
+ \@@_parse_operand: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 \@@_parse_continue:NwN
\exp_after:wN #1
\tex_romannumeral:D -`0
\exp_after:wN \exp_after:wN
@@ -2496,104 +2536,9 @@
% \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_kernel_expandable_error:nnnn
- { kernel } { fp-missing } { ? } { ~for~?: }
- \exp_after:wN @
- \exp_after:wN \@@_ternary_auxii: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
- \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) }
- \exp_after:wN \@@_parse_infix:NN
- \exp_after:wN #1
- \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w
- \else:
- \exp_after:wN @
- \exp_after:wN \@@_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 @
- {
- \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma }
- @ \@@_parse_stop_until:N
- }
-\group_end:
-% \end{macrocode}
-% \end{macro}
+% ^^A end[todo]
%
-% \section{Messages}
+% \subsection{Messages}
%
% \begin{macrocode}
\__msg_kernel_new:nnn { kernel } { unknown-fp-word }