diff options
author | Karl Berry <karl@freefriends.org> | 2013-11-22 00:05:28 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-11-22 00:05:28 +0000 |
commit | 4efb0e288e61307337a1593f83dead03178acc61 (patch) | |
tree | 1de51740df3962c81edef97135bac249b41d3fe1 /Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx | |
parent | 9dd6af228b319f176777f85b0b551eda0df6cfbe (diff) |
l3 (19nov13)
git-svn-id: svn://tug.org/texlive/trunk@32204 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx | 2895 |
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 } |