% \iffalse meta-comment % %% File: l3fp-parse.dtx Copyright (C) 2011-2012 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% This file is part of the "l3kernel bundle" (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX Project Team. %% %% ----------------------------------------------------------------------- %% % %<*driver> \RequirePackage{l3bootstrap} \GetIdInfo$Id: l3fp-parse.dtx 4339 2012-11-24 19:16:43Z joseph $ {L3 Floating-point expression parsing} \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{The \textsf{l3fp-parse} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Floating point expression parsing} % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % \date{Released \filedate} % % \maketitle % % ^^A begin[todo] % % ^^A To typeset the examples of expansion control, I'm using a hand-made % ^^A environment. % \newcommand{\fpOperation}[1] % {\textcolor[rgb]{.6,.2,.2}{\ttfamily#1}} % \newcommand{\fpPrecedence}[1] % {\textcolor[rgb]{.2,.2,.6}{\ttfamily#1}} % \newcommand{\fpExpand}[2] % {\underline{\textcolor{red}{#1{#2}}}} % \newenvironment{l3fp-code-example} % {\begin{quote}^^A % \edef\^{\string^}^^A % \let\*\fpExpand % \let\o\fpOperation % \let\p\fpPrecedence % \def\!{\begingroup\def\!{\endgroup\par}\color[gray]{0.5}}^^A % \ttfamily\frenchspacing % }{\end{quote}} % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3fp-parse} implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=fp> % \end{macrocode} % % \section{Precedences} % % In order of evaluation (some distinctions are irrelevant for the order % of evaluation, but serve as signals). % \begin{itemize} % \item[32] Juxtaposition for implicit multiplication. % \item[16] Function calls with multiple arguments. % \item[15] Function calls expecting exactly one argument. % \item[14] Binary |**| and |^| (right to left). % \item[12] Unary |+|, |-|, |!| (right to left). % \item[10] Binary |*|, |/| and |%|. % \item[9] Binary |+| and |-|. % \item[7] Comparisons. % \item[5] Logical \texttt{and}, denoted by |&&|. % \item[4] Logical \texttt{or}, denoted by \verb*+||+. % \item[3] Ternary operator |?:|, piece |?|. % \item[2] Ternary operator |?:|, piece |:|. % \item[1] Commas, and parentheses accepting commas. % \item[0] Parentheses expecting exactly one argument. % \item[-1] Start and end of the expression. % \end{itemize} % % ^^A todo: ask SO when sNaN can arise. % % \section{Evaluating an expression} % % \begin{macro}[EXP, int]{\@@_parse:n} % \begin{syntax} % \cs{@@_parse:n} \Arg{floating point expression} % \end{syntax} % This \texttt{f}-expands to the internal floating point number % obtained by evaluating the \meta{floating point expression}. During % this evaluation, each token is fully \texttt{f}-expanded. % \begin{texnote} % Registers (integers, toks, etc.) are automatically unpacked, % without requiring a function such as \cs{int_use:N}. Invalid % tokens remaining after \texttt{f}-expansion will lead to % unrecoverable low-level TeX errors.\footnote{Bruno: describe what % happens in cases like $2\cs{c_three} = 6$.} % \end{texnote} % \end{macro} % % \section{Work plan}\label{subsec:fp-parse-workplan} % % The task at hand is non-trivial, and some previous failed attempts have % shown me that the code ends up giving unreadable logs, so we'd better get % it (almost) right the first time. Let us thus first discuss precisely % the design before starting to write the code. To simplify matters, % we first consider expressions with integers only. % % \subsection{Storing results} % % The main issue in parsing expressions expandably is: \enquote{where % in the input stream should the result be put?} % % One option is to place the result at the end of the expression, % but this has several drawbacks: % \begin{itemize} % \item firstly it means that for long expressions we would be reaching % all the way to the end of the expression at every step of the % calculation, which can be rather expensive; % \item secondly, when parsing parenthesized sub-expressions, we would % naturally place the result after the corresponding closing parenthesis. % But since \cs{@@_parse:n} does not assume that its argument is expanded, % this closing parenthesis may be hidden in a macro, and not present yet, % causing havoc. % \end{itemize} % % The other natural option is to store the result at the start of the % expression, and carry it as an argument of each macro. This does not % really work either: in order to expand what follows on the input stream, % we need to skip at each step over all the tokens in the result using % \cs{exp_after:wN}. But this requires adding many \cs{exp_after:wN} to % the result at each step, also an expensive process. % % Hence, we need to go for some fine expansion control: the result is % stored \emph{before} the start\ldots{} A toy model that illustrates this % idea is to try and add some positive integers which may be hidden % within macros, or registers. Assume that one number has already been % found, and that we want to parse the next number. The current status % of the code may look as follows. % \begin{quote}\ttfamily % \cs{exp_after:wN} \cs{add:ww} % \cs{__int_value:w} 12345 \cs{exp_after:wN} ; \newline % \cs{tex_romannumeral:D} -`0 \cs{clean:w} \meta{stuff} % \end{quote} % Hitting this construction by one step of expansion expands % \cs{exp_after:wN}, which triggers the primitive \cs{__int_value:w}, % which reads an integer, \texttt{12345}. This integer is unfinished, % causing the second \cs{exp_after:wN} to expand, and trigger % the construction \cs{tex_romannumeral:D} |-`0|, which f-expands % \cs{clean:w} (see \pkg{l3expan.dtx} for an explanation). Assume % then that \cs{clean:w} is such that it expands \meta{stuff} to % \emph{e.g.}, |333444;|. Once \cs{clean:w} is done expanding, we % will obtain essentially % \begin{quote}\ttfamily % \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; 333444 ; % \end{quote} % where in fact \cs{exp_after:wN} has already been expanded, and % \cs{__int_value:w} has already seen \texttt{12345}. Now, % \cs{__int_value:w} sees the \texttt{;}, and stops expanding, and % we are left with % \begin{quote}\ttfamily % \cs{add:ww} 12345 ; 333444 ; % \end{quote} % which can safely perform the addition by grabbing two arguments % delimited by \texttt{;}. % % On this toy example, we could note that if we were to continue % parsing the expression, then the following number should also % be cleaned up before the next use of a binary operation such as % \cs{add:ww}. Just like \cs{__int_value:w} \texttt{12345} % \cs{exp_after:wN} \texttt{;} expanded what follows once, we need % \cs{add:ww} to do the calculation, and in the process to expand % the following once. This is also true in our real application: % all the functions of the form \cs{@@_..._o:ww} expand what % follows once. This comes at the cost of leaving tokens in the % input stack, and we will need to be careful to waste as little % as possible of this precious memory. % % \subsection{Precedence} % % A major point to keep in mind when parsing expressions is that % different operators have different precedence. The true analog % of our toy \cs{clean:w} macro must thus take care of that. For % definiteness, let us assume that the operation which prompted % \cs{clean:w} was a multiplication. Then \cs{clean:w} (expand % and) read digits until the number is ended by some operation. % If this is \texttt{+} or~\texttt{-}, then the multiplication % should be calculated next, so \cs{clean:w} can simply decide % that its job is done. However, if the operator we find is |^|, % then this operation must be performed before returning control % to the multiplication. This means that we need to \cs{clean:w} % the number following |^|, and perform the calculation, then just % end our job. % % Hence, each time a number is cleaned, the precedence of the % following operation must be compared to that of the previous % operation. The process of course has to happen recursively. % For instance, |1+2^3*4| would involve the following steps. % \begin{itemize} % \item |1| is cleaned up. % \item |2| is cleaned up. % \item The precedences of |+| and |^| are compared. Since the % latter is higher, the second operand of |^| should be cleaned. % \item |3| is cleaned up. % \item The precedences of |^| and |*| are compared. Since the % former is higher, the cleaning step stops. % \item Compute |2^3 = 8|. % \item We now have |1+8*4|, and the operation |+| is still % looking for a second operand. Clean |8|. % \item The precedences of |+| and |*| are compared. Since the % latter is higher, the second operand of |*| should be cleaned. % \item |4| is cleaned up, and the end of the expression is reached. % \item Compute |8*4 = 32|. % \item We now have |1+8*4|, and the operation |+| is still % looking for a second operand. Clean |32|, and reach the end % of the expression. % \item Compute |1+32 = 33|. % \end{itemize} % Here, there is some (expensive) redundant work: the results of % computations should not need to be cleaned again. Thus the true definition % is slightly more elaborate. % % The precedence of |(| and |)| are defined to be equal, and smaller than % the precedence of |+| and |-|, itself smaller than |*| and |/|, smaller, % finally, then the power operator |**| (or |^|). % % % \subsection{Infix operators} % % The implementation that was chosen is slightly wasteful: it causes % more nesting than necessary. ^^A todo: clarify. % However, it is simpler to implement and to explain than a slightly % optimized variant. ^^A todo: implement optimized version; compare. % % The cornerstone of that method is a pair of functions, % \cs{until} and \cs{one}, which both take as their first % argument the precedence (an integer) of the last operation. % The f-expansion of % \begin{quote} % \cs{until} \meta{prec} \cs{one} \meta{prec} \meta{stuff} % \end{quote} % is the internal floating point obtained by \enquote{cleaning} % numbers which follow in the input stream, and performing % computations until reaching an operation with a precedence % less than or equal to \meta{prec}. This is followed by a control % sequence of the form \cs{infix_?}, namely, % \begin{quote} % \meta{floating point} \cs{infix_?} % \end{quote} % where |?| is the operation following that number in the input % stream (we thus know that this operation has at most the % precedence \meta{prec}, otherwise it would have been performed % already). % % How is that expansion achieved? First, \cs{one} \meta{prec} % reads one \meta{floating point} number, and converts it to an % internal form, then the following operation, say |*|, is % packed in the form \cs{infix_*}, which is fed the \meta{prec}. % This function (one per infix operator) compares \meta{prec} % with the precedence of the operator we just read (here |*|). % If \meta{prec} is higher, our job is finished, and \cs{one} % leaves \cs{@@_parse_stop_until:N} so that \cs{until} knows to stop. % Otherwise, \cs{infix_*} triggers a new pair % \cs{until} \meta{prec(*)} \cs{one} \meta{prec(*)}, % which produces the second operand \meta{floating point_2} % for the multiplication: % \begin{quote} % \cs{until} \meta{prec} \meta{floating point} \newline % \texttt{...} \meta{floating point_2} |;| \cs{infix_?} % \end{quote} % The dots are \cs{@@_parse_apply_binary:NwNwN} |*|. The boolean % tells \cs{until} that it is not done, and it expands % (essentially) to % \begin{quote} % \cs{until} \meta{prec} % \cs{@@_*_o:ww} \meta{floating point} \meta{floating point_2} % \cs{tex_romannumeral:D} \texttt{-`0} \cs{infix_?} \meta{prec} % \end{quote} % making \TeX{} expand \cs{@@_*_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 disinction between the \meta{precedence} % \fpPrecedence{+}, the operation \fpOperation{+}, and the character % token |+| should not matter. It is only required to accomodate for % multi-token infix operators such as |**|: indeed, when controlling % expansion, we need to skip over those tokens using \cs{exp_after:wN}, % and this only skips one token. Thus |**| needs to be replaced by a % single token (either its precedence or its calculating function, % depending on the place). % % To end the computation cleanly, we add a trailing right % parenthesis, and give |(| and |)| the lowest precedence, % so that \cs{until}\fpPrecedence{(} \cs{one}\fpPrecedence{(} % reads numbers and performs operations until meeting a right % parenthesis. This is discussed more precisely in the next section. % % \begin{l3fp-code-example} % \cs{until}\p( \*\cs{one}\p( \! 11 + 2**3 * 5 - 9 )\! % \cs{until}\p( 1 \*\cs{one}\p( \! 1 + 2**3 * 5 - 9 )\! % \cs{until}\p( 11 \*\cs{one}\p( \! + 2**3 * 5 - 9 )\! % \cs{until}\p( 11; \*\cs{infix_+}\p( \! 2**3 * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 2**3 * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2 \*\cs{one}\p+ \! **3 * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; \*\cs{infix_**}\p+ \! 3 * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; % F \o{**} \cs{until}\p{**} \*\cs{one}\p{**} \! 3 * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; % F \o{**} \cs{until}\p{**} 3 \*\cs{one}\p{**} \! * 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; % F \o{**} \cs{until}\p{**} 3; \*\cs{infix_*}\p{**} \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; % F \o{**} \*\cs{until}\p{**} 3; T \cs{infix_*} \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 2; % F \o{**} 3; \cs{infix_*} \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{**} 2; 3; % \cs{infix_*}\p+ \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; \*\cs{infix_*}\p+ \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; % F \o* \cs{until}\p* \*\cs{one}\p* \! 5 - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; % F \o* \cs{until}\p* 5 \*\cs{one}\p* \! - 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; % F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; % F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\! % \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 8; F \o* 5; \cs{infix_-} \! 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{*} 8; 5; \cs{infix_-}\p+ \! 9 )\! % \cs{until}\p( 11; F \o+ \cs{until}\p+ 40; \*\cs{infix_-}\p+ \! 9 )\! % \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 40; T \cs{infix_-} \! 9 )\! % \*\cs{until}\p( 11; F \o+ 40; \cs{infix_-} \! 9 )\! % \cs{until}\p( \*\o{+} 11; 40; \cs{infix_-}\p( \! 9 )\! % \cs{until}\p( 51; \*\cs{infix_-}\p( \! 9 )\! % \cs{until}\p( 51; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\! % \cs{until}\p( 51; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\! % \cs{until}\p( 51; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\! % \cs{until}\p( 51; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\! % \*\cs{until}\p( 51; F \o- 9; \cs{infix_)} \!\! % \cs{until}\p( \*\o{-} 51; 9; \cs{infix_)}\p( \!\! % \cs{until}\p( 42; \*\cs{infix_)}\p( \!\! % \*\cs{until}\p( 42; T \cs{infix_)} \!\! % 42; \cs{infix_)} \!\! % \end{l3fp-code-example} % % The only missing step is to clean the output by removing \cs{infix_)}, % and possibly checking that nothing else remains. % % \subsection{Prefix operators, parentheses, and functions} % % Prefix operators (typically the unary |-|) and parentheses are % taken care of by the same mechanism, and functions (\texttt{sin}, % \texttt{exp}, etc.) as well. Finding the argument of the unary % |-|, for instance, is very similar to grabbing the second operand % of a binary infix operator, with a small subtelty on precedence % explained below. Once that argument is found, its sign can be % flipped. A left parenthesis is just a prefix operator which % removes the closing parenthesis (with some extra checks). % % Detecting prefix operators is done by \cs{one}. Before looking % for a number, it tests the first character. If it is a digit, a % dot, or a register, then we have a number. Otherwise, it is put % in a function, \cs{prefix_?} (where |?| is roughly that first % character), which is expanded. For instance, with a left % parenthesis we would have the following. % \begin{l3fp-code-example} % \*\cs{one}\p* \! ( 2 + 3 ) \! % \*\cs{prefix_(}\p* \! 2 + 3 ) \! % \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) \! % ... \!\! % \o(\p* 5; \cs{infix_)} \! \! % \end{l3fp-code-example} % As usual, the \cs{until}--\cs{one} pair reads and compute % until reaching an operator of precedence at most \fpPrecedence{(}. % Then \fpOperation{(} removes \cs{infix_)} and looks ahead for % the next operation, comparing its precedence with the precedence % \fpPrecedence{*} of the previous operation (in fact, this comparison % is done by the relevant \cs{infix_?} built from the next operation). % % To support multi-character function (and constant) names, we % may need to put more than one character in the \cs{prefix_?} % construction. See implementation for details. % % Note that contrarily to \cs{infix_?} functions, the \cs{prefix_?} % functions perform no test on their argument (which is once more % the previous precedence), since we know that we need a number, % and must never stop there. % % Functions are implemented as prefix operators with infinitely high % precedence, so that their argument is the first number that can % possibly be built. For instance, something like the following could % happen in a computation % \begin{l3fp-code-example} % \*\cs{one}\p* \! sqrt 4 + 3 ) \! % \*\cs{prefix_sqrt}\p* \! 4 + 3 ) \! % \o{sqrt}\p* \cs{until}\p{$\infty$} \*\cs{one}\p{$\infty$} \! 4 + 3 ) \! % ... \!\! % \o{sqrt}\p* 4; \cs{infix_+} \! 3 ) \! % 2; \*\cs{infix_+}\p* \! 3 ) \! % \end{l3fp-code-example} % % Lonely example, to be put somewhere: |2+sin 1 * 3| is $2+(\sin(1)\times 3)$. % % A further complication arises in the case of the unary |-| sign: % |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. Easy, just give % |-| a lower precedence, equal to that of the infix |+| and |-|. % Unfortunately, this fails in subtle cases such as |3**-2*4|, % yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. % In fact, a unary |-| should only perform operations whose precedence % is greater than that of the last operation, as well as % |-|.\footnote{Taking into account the precedence of \texttt{-} itself % only matters when it follows a left parenthesis: % \texttt{(-2*4+3)} should give \texttt{((-8)+3)}, not \texttt{(-(8+3))}.} % Thus, \cs{prefix_-} \meta{prec} expands to something like % \begin{l3fp-code-example} % \o- \meta{prec} \cs{until}\p? \*\cs{one} \p? % \end{l3fp-code-example} % where \fpPrecedence{?} is the maximum of \meta{prec} and the % precedence of |-|. Once the argument of |-| is found, \fpOperation{-} % gets its opposite, and leaves it for the previous operation to use. % % An example with parentheses. % % \begin{l3fp-code-example} % \cs{until}\p( \*\cs{one}\p( \! 11 * ( 2 + 3 ) - 9 )\! % \cs{until}\p( 1 \*\cs{one}\p( \! 1 * ( 2 + 3 ) - 9 )\! % \cs{until}\p( 11 \*\cs{one}\p( \! * ( 2 + 3 ) - 9 )\! % \cs{until}\p( 11; \*\cs{infix_*}\p( \! ( 2 + 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{one}\p* \! ( 2 + 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{prefix_(}\p* \! 2 + 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2 \*\cs{one}\p( \! + 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; \*\cs{infix_+}\p( \! 3 ) - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 3)-9)\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3 \*\cs{one}\p+ \! )-9)\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3; \*\cs{infix_)}\p+ \! -9)\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \*\cs{until}\p+ 3; T \cs{infix_)} \! -9)\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 2; F \o+ 3; \cs{infix_)} \! - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\o+ 2; 3; \cs{infix_)}\p( \! - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 5; \*\cs{infix_)}\p( \! - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 5; T \cs{infix_)} \! - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* \*\o(\p* 5; \cs{infix_)} \! - 9 )\! % \cs{until}\p( 11; F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\! % \cs{until}\p( 11; F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\! % \*\cs{until}\p( 11; F \o* 5; \cs{infix_-} \! 9 )\! % \cs{until}\p( \*\o* 11; 5; \cs{infix_-}\p( \! 9 )\! % \cs{until}\p( 55; \* \cs{infix_-}\p( \! 9 )\! % \cs{until}\p( 55; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\! % \cs{until}\p( 55; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\! % \cs{until}\p( 55; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\! % \cs{until}\p( 55; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\! % \*\cs{until}\p( 55; F \o- 9; \cs{infix_)} \!\! % \cs{until}\p( \*\o- 55; 9; \cs{infix_)}\p( \!\! % \cs{until}\p( 47; \*\cs{infix_)}\p( \!\! % \*\cs{until}\p( 47; T \cs{infix_)} \!\! % 47; \cs{infix_)} \!\! % \end{l3fp-code-example} % % The end of this (sub)section was not revised yet % % \begin{itemize} % \item If it is a sign (|-| or |+|), then any following sign will be % combined with this initial sign, forming \cs{prefix_+} or \cs{prefix_-}. % \item If it is a letter, then any following letter is grabbed, forming % for instance \cs{prefix_sin} or \cs{prefix_sinh}. % \item Otherwise, only one token\footnote{Some support for multi-character % prefix operator may be added in the future, but right now, I don't % see a use for it. Perhaps, for including comments inside % the computation itself??} is grabbed, for instance \cs{prefix_(}. % \end{itemize} % %^^A todo: make sure that's correct?? % % Functions may take several arguments, possibly an unknown % number\footnote{Keyword argument support may be added later.}, % for instance \texttt{round(1.23456,2)}. % \begin{itemize} % \item \texttt{round} is made into \cs{prefix_round}, which tries to % grab one number using \cs{one}. % \item This builds \cs{prefix_(}, which uses \cs{one} to grab one % number, calculating as necessary. The comma is given the same % precedence as parentheses, and thus ends the calculation of the % argument of \texttt{round}. % \item \texttt{round} now has its first argument. It can check whether % the argument was closed by |,| or |)|, and branch accordingly. % \item If it was a comma, then the first argument is skipped over, % through an expensive set of \cs{exp_after:wN}, and the second % argument can be grabbed. Here it is simply an integer, easier % to parse by building upon \cs{etex_numexpr:D}. % \item The closing parenthesis (or another comma) is seen, and the % control is given back to \cs{prefix_round}. % \end{itemize} % % \subsection{Type detection} % % The type of data should be detected by reading the first few tokens, % before calling a type-specific function to parse it. Or % should the type be obtained after the semicolon which indicates the % end of the thing? And placed there? % % ^^A todo: what did I mean in this paragraph? % Also to grab exponents correctly, build \cs{@@_:w} when seeing % some non-numeric |abc| while still looking to complete a number (or % other data). Then, if \cs{@@_postfix__:w} exists, use it. % % The internal representation of floating point numbers is quite % untypable, and we provide here the tools to convert from a more % user-friendly representation to internal floating point numbers, % and for various other conversions. Every floating point operation % calls those functions to normalize the input, so they must be % optimized. % % \section{Internal representation} % % Internally, a floating point number \meta{X} is a % token list containing % \begin{quote} % \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \meta{body} |;| % \end{quote} % Let us explain each piece separately. % % Internal floating point numbers will be used in expressions, % and in this context will be subject to f-expansion. They must % leave a recognizable mark after \texttt{f}-expansion, to prevent the % floating point number from being re-parsed. Thus, \cs{s_@@} % is simply another name for \tn{relax}. % % Since floating point numbers are always accessed by the various % operations using f-expansion, we can safely let them be protected: % \texttt{x}-expansion will then leave them untouched. However, when % used directly without an accessor function, floating points should % produce an error. \cs{s_@@} will do nothing, and \cs{@@_chk:w} % produces an error. % % The (decimal part of the) IEEE-754-2008 standard requires the % format to be able to represent special floating point numbers % besides the usual positive and negative cases. The various % possibilities will be distinguished by their \meta{case}, which % is a single digit:\footnote{Bruno: I need to implement subnormal % numbers. Also, quiet and signalling \texttt{nan} must be better % distinguished.} % \begin{itemize} % \item[0] zeros: |+0| and |-0|, % \item[1] \enquote{normal} numbers (positive and negative), % \item[2] infinities: |+inf| and |-inf|, % \item[3] quiet and signalling \texttt{nan}. % \end{itemize} % The \meta{sign} is |0| (positive) or |2| (negative), % except in the case of \texttt{nan}, which have $\meta{sign} = 1$. % This ensures that changing the \meta{sign} digit to $2-\meta{sign}$ % is exactly equivalent to changing the sign of the number. % % Special floating point numbers have the form % \begin{quote} % \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \cs{s_@@_...} |;| % \end{quote} % where \cs{s_@@_...} is a scan mark carrying information about how the % number was formed (useful for debugging). % % Normal floating point numbers ($\meta{case} = 1$) have the form % \begin{quote} % \cs{s_@@} \cs{@@_chk:w} 1 \meta{sign} \Arg{exponent} % \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} |;| % \end{quote} % Here, the \meta{exponent} is an integer, at most % $\cs{c_@@_max_exponent_int} = % \the\csname\detokenize{c__fp_max_exponent_int}\endcsname$ % in absolute value. The body consists in four % blocks of exactly $4$ digits, $ 0000 \leq \meta{X_i} \leq 9999$, % such that % \[ % \meta{X} % = (-1)^{\meta{sign}} 10^{-\meta{exponent}} % \sum_{i=1}^{4} \meta{X_i} 10^{-4i} % \] % and such that the \meta{exponent} is minimal. This implies % $ 1000 \leq \meta{X_1} \leq 9999 $. % % \begin{table}\centering % \caption{Internal representation of floating point numbers.} % \label{tab:fp-convert-special} % \begin{tabular}{ll} % \toprule % \multicolumn{1}{c}{Representation} & Meaning \\ % \midrule % 0 0 \cs{s_@@_...} \texttt{;} & Positive zero. \\ % 0 2 \cs{s_@@_...} \texttt{;} & Negative zero. \\ % 1 0 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} % & Positive floating point. \\ % 1 2 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} % & Negative floating point. \\ % 2 0 \cs{s_@@_...} \texttt{;} & Positive infinity. \\ % 2 2 \cs{s_@@_...} \texttt{;} & Negative infinity. \\ % 3 1 \cs{s_@@_...} \texttt{;} & Quiet \texttt{nan}. \\ % 3 1 \cs{s_@@_...} \texttt{;} & Signalling \texttt{nan}. \\ % \bottomrule % \end{tabular} % \end{table} % % \section{Internal parsing functions} % % \begin{macro}[EXP, int]{\@@_parse_until:Nw} % \begin{syntax} % \cs{tex_romannumeral:D} \cs{@@_parse_until:Nw} \meta{precedence} \cs{@@_parse_expand:w} \meta{tokens} % \end{syntax} % Reads the \meta{tokens}, performing every computation with a % precedence higher than \meta{precedence}, then expands to % \begin{syntax} % \meta{objects} |@| \cs{@@_parse_infix_\meta{operation}:N} \ldots{} % \end{syntax} % where the \meta{op} is the first operation with a lower precedence, % possibly \texttt{end}. % \end{macro} % % \begin{macro}[EXP, int]{\@@_parse_operand:Nw} % \begin{syntax} % \cs{@@_parse_operand:Nw} \meta{precedence} \ldots{} % \end{syntax} % If the following \meta{operation} has a precedence higher than % \meta{precedence}, expands to % \begin{syntax} % \meta{object_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object_2} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{} % \end{syntax} % and otherwise expands to % \begin{syntax} % \meta{object} |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} \ldots{} % \end{syntax} % \end{macro} % % \begin{macro}[EXP, int]{\@@_parse_infix_\meta{operation}:N} % \begin{syntax} % \cs{@@_parse_infix_\meta{operation}:N} \meta{precedence} % \end{syntax} % If the \meta{op} has a precedence higher than \meta{precedence}, expands to % \begin{syntax} % |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object} |@| \cs{@@_parse_infix_\meta{operation_2}:N} % \end{syntax} % Otherwise expands to % \begin{syntax} % |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} % \end{syntax} % \end{macro} % % ^^A end[todo] % % \subsection{Expansion control} % % At each step in reading a floating point expression, we wish to % perform \texttt{f}-expansion. Normally, spaces stop this % \texttt{f}-expansion. This can be problematic: for instance, the % macro |\X| below will not be expanded if we simply do % \texttt{f}-expansion. % \begin{verbatim} % \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} } % \ExplSyntaxOff % \test { 1 + \X } % \end{verbatim} % To avoid this problem, at every step, we do essentially what % \cs{use:f} would do: take an argument, put it back in the input % stream, then \texttt{f}-expand it. This is not a complete solution, % since a macro's expansion could contain leading spaces which will stop % the \texttt{f}-expansion before further macro calls are performed. % However, in practice it should be enough: in particular, floating % point numbers will correctly be expanded to the underlying \cs{s_@@} % \ldots{} structure. % %^^A begin[todo] % Floating point expressions should behave as much as possible like % \eTeX{}-based integer expressions and dimension expressions. In % particular, full-expansion should be performed as the expression is % read, token by token, forcing the expansion of protected macros, and % ignoring spaces. % % Full expansion can be done with \cs{tex_romannumeral:D} |-`0|. % Unfortunately, this expansion is stopped by spaces. Thus using simply % this will fail on |\fp_eval:n { 1 + ~ \l_tmpa_fp }| since the floating % point variable will not be expanded. Of course, spaces will not % appear in a code setting, but may very easily come in document-level % input, from which some expressions may come. We can avoid being % stopped by such explicit space characters (and by some braces) if we % add \cs{use:n} after~|-`0|. % % Testing if a character token |#1| is a digit can be done using % \begin{verbatim} % \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: % true code % \else: % false code % \fi: % \end{verbatim} % To exclude |0|, replace \cs{c_nine} by \cs{c_ten}. The use of % \cs{token_to_str:N} ensures that a digit with any catcode is detected. % %^^A end[todo] % % \begin{macro}[aux, rEXP]{\@@_parse_expand:w} % \begin{syntax} % \cs{tex_romannumeral:D} \cs{@@_parse_expand:w} \meta{tokens} % \end{syntax} % This function must always come within a \tn{romannumeral} expansion. % The \meta{tokens} should be the part of the expression that we have % not yet read. This requires in particular closing all conditionals % properly before expanding. % \begin{macrocode} \cs_new:Npn \@@_parse_expand:w #1 { -`0 #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_parse_return_semicolon:w} % This very odd function swaps its position with the following % \cs{fi:} and removes \cs{@@_parse_expand:w} normally responsible for % expansion. That turns out to be useful. % \begin{macrocode} \cs_new:Npn \@@_parse_return_semicolon:w #1 \fi: \@@_parse_expand:w { \fi: ; #1 } % \end{macrocode} % \end{macro} % % \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} % \end{syntax} % Grabs the pieces of the stringified \meta{token} which lies after % the first |s__fp|. If the \meta{token} does not contain that % string, the result is |_?|. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \S \char_set_catcode_other:N \F \char_set_catcode_other:N \P \char_set_lccode:nn { `\- } { `\_ } \tl_to_lowercase:n { \group_end: \cs_new:Npn \@@_type_from_scan:N #1 { \exp_after:wN \@@_type_from_scan:w \token_to_str:N #1 \q_mark S--FP-? \q_mark \q_stop } \cs_new:Npn \@@_type_from_scan:w #1 S--FP #2 \q_mark #3 \q_stop {#2} } % \end{macrocode} % \end{macro} % % \subsection{Reading digits} % % \begin{macro}[rEXP, aux] % { % \@@_parse_digits_vii:N , % \@@_parse_digits_vi:N , % \@@_parse_digits_v:N , % \@@_parse_digits_iv:N , % \@@_parse_digits_iii:N , % \@@_parse_digits_ii:N , % \@@_parse_digits_i:N % } % These functions must be called within an \cs{__int_value:w} or % \cs{__int_eval:w} construction. The first token which follows must be % \texttt{f}-expanded prior to calling those functions. The functions % read tokens one by one, and output digits into the input stream, % until meeting a non-digit, or up to a number of digits equal to % their index. The full expansion is % \begin{quote} % \meta{digits} |;| \meta{filling 0} |;| \meta{length} % \end{quote} % where \meta{filling 0} is a string of zeros such that \meta{digits} % \meta{filling 0} has the length given by the index of the function, % and \meta{length} is the number of zeros in the \meta{filling 0} % string. Each function puts a digit into the input stream and calls % the next function, until we find a non-digit. We are careful to % pass the tested tokens through \cs{token_to_str:N} to normalize % their category code. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 #2 #3 { \cs_new:cpn { @@_parse_digits_ #1 :N } ##1 { \if_int_compare:w \c_nine < 1 \token_to_str:N ##1 \exp_stop_f: \token_to_str:N ##1 \exp_after:wN #2 \tex_romannumeral:D \else: \@@_parse_return_semicolon:w #3 ##1 \fi: \@@_parse_expand:w } } \@@_tmp:w {vii} \@@_parse_digits_vi:N { 0000000 ; 7 } \@@_tmp:w {vi} \@@_parse_digits_v:N { 000000 ; 6 } \@@_tmp:w {v} \@@_parse_digits_iv:N { 00000 ; 5 } \@@_tmp:w {iv} \@@_parse_digits_iii:N { 0000 ; 4 } \@@_tmp:w {iii} \@@_parse_digits_ii:N { 000 ; 3 } \@@_tmp:w {ii} \@@_parse_digits_i:N { 00 ; 2 } \@@_tmp:w {i} \@@_parse_digits_:N { 0 ; 1 } \cs_new_nopar:Npn \@@_parse_digits_:N { ; ; 0 } % \end{macrocode} % \end{macro} % % \subsection{Parsing one operand} % % At the start of an expression, or just following a binary operation or % a function call, we are looking for an operand. This can be an % explicit floating point number, a floating point variable, a \TeX{} % register, a function call such as \texttt{sin(3)}, a parenthesized % expression, \emph{etc.} We distinguish the various cases by their % first token after \texttt{f}-expansion: % \begin{itemize} % \item \cs{tex_relax:D} in some form. That can be an internal % floating point, a premature end, or an unitialized register. % \item A register. We interpret this as the significand of a floating % point number. This is subtely different from unpacking it, for % instance, \texttt{\cs{c_minus_one}**2} gives $1$, while % \texttt{-1**2} gives $-1$. % \item A digit, or a dot. That marks the start of the significand for % a floating point number. % \item A letter (lower or upper-case), which starts an identifier, % either a constant or a function (possibly unknown). % \item |+|, |-|, or |!|, unary operators, which resume looking for a % floating point number before acting on it. % \item |(|, which makes us parse a subexpression until the % matching~|)|. % \item Other characters such as |'| or |"| may be given a meaning % later. Characters such as |*| or |/| have a meaning as infix % operators but are not valid when we are looking for an operand: for % instance, |3+*4| is not valid. % \end{itemize} % A category code test separates the first two cases from the others, % and they are further distinguished with a meaning test. We then % single out digits. Letters are detected using their character code. % All other characters are taken care of by building a csname from that % character and using it to continue parsing. Unknown characters lead % to an error. % % \begin{macro}[int, EXP]{\@@_parse_operand:Nw} % Function called \cs{one} at other places. It grabs one operand, and % packs the symbol that follows in an \cs{infix_} csname. |#1| is the % previous \meta{precedence}, and |#2| the first character of the % operand (already \texttt{f}-expanded). % \begin{macrocode} \cs_new:Npn \@@_parse_operand:Nw #1 #2 { \if_catcode:w \tex_relax:D #2 \if_meaning:w \tex_relax:D #2 \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_operand_relax:NN \else: \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_operand_register:NN \fi: \else: \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_operand_digit:NN \else: \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_operand_other:NN \fi: \fi: #1 #2 } % \end{macrocode} % \end{macro} % % ^^A todo: rounding of negative dimensions is probably wrong. % \begin{macro}[aux, EXP] % {\@@_parse_operand_register:NN, \@@_parse_operand_register_aux:www} % Find the exponent following the register |#2|, then combine the % value of |#2| (mapping |1pt| to $1$) with the exponent to produce a % floating point number. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \P \char_set_catcode_other:N \T \tl_to_lowercase:n { \group_end: \cs_new:Npn \@@_parse_operand_register:NN #1#2 { \exp_after:wN \@@_parse_infix_after_operand:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \@@_parse_operand_register_aux:www \tex_the:D \exp_after:wN #2 \exp_after:wN P \exp_after:wN T \exp_after:wN \q_stop \__int_value:w \@@_parse_exponent:N } \cs_new:Npn \@@_parse_operand_register_aux:www #1 PT #2 \q_stop #3 ; { \@@_parse:n { #1 e #3 } } } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, EXP] % { % \@@_parse_operand_relax:NN, % \@@_parse_exp_after_f:nw, % \@@_parse_exp_after_mark_f:nw, % \@@_parse_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}. % \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. % \item \cs{s_@@_mark} is a premature end, we call % \cs{@@_parse_exp_after_mark_f:nw}, which triggers the % appropriate error. % \item For a control sequence not containing |\s__fp|, we call % \cs{@@_parse_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 % correctly expanded. % \begin{macrocode} \cs_new:Npn \@@_parse_operand_relax:NN #1#2 { \cs:w @@_parse_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 { \__msg_kernel_expandable_error:nn { kernel } { fp-early-end } \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1 } \cs_new:cpn { @@_parse_exp_after_?_f:nw } #1#2 { \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#2} \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1 } % \end{macrocode} % \end{macro} % % ^^A begin[todo] % % \begin{macro}[aux, EXP]{\@@_parse_operand_other:NN} % The interesting bit is \cs{@@_parse_operand_other:NN}. It separates % letters from non-letters and builds the appropriate \cs{prefix} % function. If it is not defined (is \cs{tex_relax:D}), make it % a signalling \texttt{nan}. We don't look for an argument, as the % unknown \enquote{prefix} can also be a (mistyped) constant such % as \texttt{Inf}. % \begin{macrocode} \cs_new:Npn \@@_parse_operand_other:NN #1 #2 { \if_int_compare:w \__int_eval:w \tex_uccode:D `#2 / 26 = \c_three \exp_after:wN \@@_parse_operand_other_word_aux:Nw \exp_after:wN #1 \tex_romannumeral:D \exp_after:wN \@@_parse_letters:NN \exp_after:wN #2 \tex_romannumeral:D \else: \exp_after:wN \@@_parse_operand_other_prefix_aux:NNN \exp_after:wN #1 \exp_after:wN #2 \cs:w @@_parse_prefix_#2:Nw \exp_after:wN \cs_end: \tex_romannumeral:D \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_letters:NN #1#2 { \exp_after:wN \c_zero \exp_after:wN #1 \tex_romannumeral:D \if_int_compare:w \if_catcode:w \tex_relax:D #2 \c_zero \else: \__int_eval:w \tex_uccode:D `#2 / 26 \fi: = \c_three \exp_after:wN \@@_parse_letters:NN \exp_after:wN #2 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN \c_zero \exp_after:wN ; \exp_after:wN #2 \fi: } \cs_new:Npn \@@_parse_operand_other_word_aux:Nw #1 #2; { \cs_if_exist_use:cF { @@_parse_word_#2:N } { \__msg_kernel_expandable_error:nnn { kernel } { unknown-fp-word } {#2} \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 \@@_parse_infix:NN } #1 } \cs_new_eq:NN \s_@@_unknown \tex_relax:D \cs_new:Npn \@@_parse_operand_other_prefix_aux:NNN #1#2#3 { \if_meaning:w \tex_relax:D #3 \exp_after:wN \@@_parse_operand_other_prefix_unknown:NNN \exp_after:wN #2 \fi: #3 #1 } \cs_new:Npn \@@_parse_operand_other_prefix_unknown:NNN #1#2#3 { \cs_if_exist:cTF { @@_parse_infix_#1:N } { \__msg_kernel_expandable_error:nnn { kernel } { fp-missing-number } {#1} \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 \@@_parse_infix:NN #3 #1 } { \__msg_kernel_expandable_error:nnn { kernel } { fp-unknown-symbol } {#1} \@@_parse_operand:Nw #3 } } % \end{macrocode} % \end{macro} % % The following forms are accepted: % \begin{itemize} % \item % \item \meta{floating point} % \item \meta{integer} |.| \meta{decimal} |e| \meta{exponent} % \end{itemize} % In both cases, \meta{signs} is a (possibly empty) string of % |+| and |-| (with any category code\footnote{Bruno: except % 1, 2, 4, 10, 13, and those which cannot be tokens (0, 5, 9), % so really, just 3, 6, 7, 8, 11, 12.}).\footnote{Bruno: % test (and implement) non-other digits.} % % In the second form, the \meta{integer} is a sequence of digits, % whose length is not limited by constraints \TeX{}'s integer % registers. It stops at the first non-digit character. The % \meta{decimal} part is formed by all digits from the dot % (if it exists) until the first non-digit character. The % \meta{exponent} part has the form \meta{exponent sign} % \meta{exponent body}, where \meta{exponent sign} is any string % of |+| or |-|, and \meta{exponent body} is a string of digits, % stopping, as usual, at the first non-digit. % % Any missing part will take the appropriate default value. % \begin{itemize} % \item A missing \meta{exponent} is considered to be zero. % \item A number with no dot has zero decimal part. % \item An empty \meta{integer} part or decimal part is zero. % \end{itemize} % % Border cases: % \begin{itemize} % \item \texttt{e1} is considered as invalid input, and gives % \texttt{qnan}.\footnote{Bruno: now just gives an error.} % This will be important once parsing expressions is % implemented, since \texttt{e-1} would be ambiguous otherwise. % \item \texttt{.e3} and \texttt{.} are zero. % \end{itemize} % % Bruno: expansion, not yet. Only f-expansion at the start, and % unpacking of registers after signs. % % % Work-plan. % \begin{itemize} % \item Remove any leading sign and build the \meta{sign} as we go. % If the next character is a letter, go to the \enquote{special} % branch, discussed later. % \item Drop leading zeros. % \item If the next character is a dot, drop some more zeros, % keeping track of how many were dropped after the dot. % Counting those gives $\meta{exp_1}<0$. Then read the decimal part % with the \cs{@@_from_str_small} functions. % \item Otherwise, $\meta{exp_1}=0$, and first read the integer part, % then the decimal part. This is implemented through the more % elaborate \cs{@@_from_str_large} functions. % \item Continuing in the same line of expansion, read the exponent % \meta{exp_2}. % \item Finally check that nothing is left.\footnote{Bruno: not done yet.} % \end{itemize} % % \begin{macro}[aux, EXP]{\@@_parse_operand_digit:NN} % \begin{macrocode} \cs_new:Npn \@@_parse_operand_digit:NN #1 { \exp_after:wN \@@_parse_infix_after_operand:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \@@_sanitize:wN \int_use:N \__int_eval:w \c_zero \@@_parse_trim_zeros:N } % \end{macrocode} % \end{macro} % % ^^A end[todo] % % \subsubsection{Trimming leading zeros} % % \begin{macro}[aux, rEXP]{\@@_parse_trim_zeros:N, \@@_parse_trim_end:w} % This function expects an already expanded token. It removes any % leading zero, then distinguished three cases: if the first non-zero % token is a digit, then call \cs{@@_parse_large:N} (the significand is % $\geq 1$); if it is |.|, then continue trimming zeros with % \cs{@@_parse_strim_zeros:N}; otherwise, our number is exactly zero, % and we call \cs{@@_parse_zero:} to take care of that case. % \begin{macrocode} \cs_new:Npn \@@_parse_trim_zeros:N #1 { \if:w 0 #1 \exp_after:wN \@@_parse_trim_zeros:N \tex_romannumeral:D \else: \if:w . #1 \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D \else: \@@_parse_trim_end:w #1 \fi: \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_trim_end:w #1 \fi: \fi: \@@_parse_expand:w { \fi: \fi: \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: \exp_after:wN \@@_parse_large:N \else: \exp_after:wN \@@_parse_zero: \fi: #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_strim_zeros:N, \@@_parse_strim_end:w} % If we have removed all digits until a period (or if the body started % with a period), then enter the \enquote{\texttt{small_trim}} loop % which outputs $-1$ for each removed $0$. Those $-1$ are added to an % integer expression waiting for the exponent. If the first non-zero % token is a digit, call \cs{@@_parse_small:N} (our significand is % smaller than~$1$), and otherwise, the number is an exact zero. % \begin{macrocode} \cs_new:Npn \@@_parse_strim_zeros:N #1 { \if:w 0 #1 - \c_one \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D \else: \@@_parse_strim_end:w #1 \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_strim_end:w #1 \fi: \@@_parse_expand:w { \fi: \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: \exp_after:wN \@@_parse_small:N \else: \exp_after:wN \@@_parse_zero: \fi: #1 } % \end{macrocode} % \end{macro} % % \subsubsection{Exact zero} % % \begin{macro}[aux, EXP]{\@@_parse_zero:} % After reading a significand of $0$, we need to remove any exponent, % then put a sign of |1| for \cs{@@_sanitize:wN}, denoting an % exact zero. % \begin{macrocode} \cs_new:Npn \@@_parse_zero: { \exp_after:wN ; \exp_after:wN 1 \__int_value:w \@@_parse_exponent:N } % \end{macrocode} % \end{macro} % % \subsubsection{Small significand} % % \begin{macro}[aux, rEXP]{\@@_parse_small:N} % This function is called after we have passed the decimal separator % and removed all leading zeros from the significand. It is followed % by a non-zero digit (with any catcode). The goal is to read up to % $16$ digits. But we can't do that all at once, because % \cs{__int_value:w} (which allows us to collect digits and continue % expanding) can only go up to $9$ digits. Hence we grab digits in % two steps of $8$ digits. Since |#1| is a digit, read seven more % digits using \cs{@@_parse_digits_vii:N}. The \texttt{small_leading} % auxiliary will leave those digits in the \cs{__int_value:w}, and grab % some more, or stop if there are no more digits. Then the % \texttt{pack_leading} auxiliary puts the various parts in the % appropriate order for the processing further up. % \begin{macrocode} \cs_new:Npn \@@_parse_small:N #1 { \exp_after:wN \@@_parse_pack_leading:NNNNNww \int_use:N \__int_eval:w 1 \token_to_str:N #1 \exp_after:wN \@@_parse_small_leading:wwNN \__int_value:w 1 \exp_after:wN \@@_parse_digits_vii:N \tex_romannumeral:D \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_small_leading:wwNN} % \begin{syntax} % \cs{@@_parse_small_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} % \end{syntax} % We leave \meta{digits} \meta{zeros} in the input stream: the % functions used to grab digits are such that this constitutes digits % $1$ through $8$ of the significand. Then prepare to pack $8$ more % digits, with an exponent shift of \cs{c_zero} (this shift is used in % the case of a large significand). If |#4| is a digit, leave it % behind for the packing function, and read $6$ more digits to reach a % total of $15$ digits: further digits are involved in the rounding. % Otherwise put $8$ zeros in to complete the significand, then look % for an exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_small_leading:wwNN 1 #1 ; #2; #3 #4 { #1 #2 \exp_after:wN \@@_parse_pack_trailing:NNNNNNww \exp_after:wN \c_zero \int_use:N \__int_eval:w 1 \if_int_compare:w \c_nine < 1 \token_to_str:N #4 \exp_stop_f: \token_to_str:N #4 \exp_after:wN \@@_parse_small_trailing:wwNN \__int_value:w 1 \exp_after:wN \@@_parse_digits_vi:N \tex_romannumeral:D \else: 0000 0000 \@@_parse_exponent:Nw #4 \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_small_trailing:wwNN} % \begin{syntax} % \cs{@@_parse_small_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} % Leave digits $10$ to $15$ (arguments |#1| and |#2|) in the input % stream. If the \meta{next~token} is a digit, it is the $16$th % digit, we keep it, then the \texttt{small_round} auxiliary considers % this digit and all further digits to perform the rounding: the % function expands to nothing or to |+1|. Otherwise, there is no % $16$-th digit, so we put a $0$, and look for an exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4 { #1 #2 \if_int_compare:w \c_nine < 1 \token_to_str:N #4 \exp_stop_f: \token_to_str:N #4 \exp_after:wN \@@_parse_small_round:NN \exp_after:wN #4 \tex_romannumeral:D \else: 0 \@@_parse_exponent:Nw #4 \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP] % { % \@@_parse_pack_trailing:NNNNNNww , % \@@_parse_pack_leading:NNNNNww , % \@@_parse_pack_carry:w % } % Those functions are expanded after all the digits are found, we took % care of the rounding, as well as the exponent. The last argument is % the exponent. The previous five arguments are $8$ digits which we % pack in groups of $4$, and the argument before that is $1$, except % in the rare case where rounding lead to a carry, in which case the % argument is $2$. The \texttt{trailing} function has an exponent % shift as its first argument, which we add to the exponent found in % the |e...| syntax. If the trailing digits cause a carry, the % integer expression for the leading digits is incremented (|+ \c_one| % in the code below). If the leading digits propagate this carry all % the way up, the function \cs{@@_parse_pack_carry:w} increments the % exponent, and changes the significand from |0000...| to |1000...|: this % is simple because such a carry can only occur to give rise to a % power of $10$. % \begin{macrocode} \cs_new:Npn \@@_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ; { \if_meaning:w 2 #2 + \c_one \fi: ; #8 + #1 ; {#3#4#5#6} {#7}; } \cs_new:Npn \@@_parse_pack_leading:NNNNNww #1 #2#3#4#5 #6; #7; { + #7 \if_meaning:w 2 #1 \@@_parse_pack_carry:w \fi: ; 0 {#2#3#4#5} {#6} } \cs_new:Npn \@@_parse_pack_carry:w \fi: ; 0 #1 { \fi: + \c_one ; 0 {1000} } % \end{macrocode} % \end{macro} % % \subsubsection{Large significand} % % Parsing a significand larger than $1$ is a little bit more difficult % than parsing small significands. We need to count the number of % digits before the decimal separator, and add that to the final % exponent. We also need to test for the presence of a dot each time we % run out of digits, and branch to the appropriate \texttt{parse_small} % function in those cases. % % \begin{macro}[aux, EXP]{\@@_parse_large:N} % This function is followed by the first non-zero digit of a % \enquote{large} significand ($\geq 1$). It is called within an % integer expression for the exponent. Grab up to $7$ more digits, % for a total of $8$ digits. % \begin{macrocode} \cs_new:Npn \@@_parse_large:N #1 { \exp_after:wN \@@_parse_large_leading:wwNN \__int_value:w 1 \token_to_str:N #1 \exp_after:wN \@@_parse_digits_vii:N \tex_romannumeral:D \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_large_leading:wwNN} % \begin{syntax} % \cs{@@_parse_large_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} % We shift the exponent by the number of digits in |#1|, namely the % target number, $8$, minus the \meta{number of zeros} (number of % digits missing). Then prepare to pack the $8$ first digits. If the % \meta{next token} is a digit, read up to $6$ more digits (digits % $10$ to $15$). If it is a period, try to grab the end of our $8$ % first digits, branching to the \texttt{small} functions since the % number of digit does not affect the exponent anymore. Finally, if % this is the end of the significand, insert the \meta{zeros} to % complete the $8$ first digits, insert $8$ more, and look for an % exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_large_leading:wwNN 1 #1 ; #2; #3 #4 { + \c_eight - #3 \exp_after:wN \@@_parse_pack_leading:NNNNNww \int_use:N \__int_eval:w 1 #1 \if_int_compare:w \c_nine < 1 \token_to_str:N #4 \exp_stop_f: \exp_after:wN \@@_parse_large_trailing:wwNN \__int_value:w 1 \token_to_str:N #4 \exp_after:wN \@@_parse_digits_vi:N \tex_romannumeral:D \else: \if:w . #4 \exp_after:wN \@@_parse_small_leading:wwNN \__int_value:w 1 \cs:w @@_parse_digits_ \tex_romannumeral:D #3 :N \exp_after:wN \cs_end: \tex_romannumeral:D \else: #2 \exp_after:wN \@@_parse_pack_trailing:NNNNNNww \exp_after:wN \c_zero \__int_value:w 1 0000 0000 \@@_parse_exponent:Nw #4 \fi: \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_parse_large_trailing:wwNN} % \begin{syntax} % \cs{@@_parse_large_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} % We have just read $15$ digits. If the \meta{next token} is a digit, % then the exponent shift caused by this block of $8$ digits is $8$, % first argument to the \texttt{pack_trailing} function. We keep the % \meta{digits} and this $16$-th digit, and find how this should be % rounded using \cs{@@_parse_large_round:NN}. Otherwise, the exponent % shift is the number of \meta{digits}, $7$ minus the \meta{number of % zeros}, and we test for a decimal point. This case happens in % |123451234512345.67| with exactly $15$ digits before the decimal % separator. Then branch to the appropriate \texttt{small} auxiliary, % grabbing a few more digits to complement the digits we already % grabbed. Finally, if this is truly the end of the significand, look % for an exponent after using the \meta{zeros} and providing a $16$-th % digit of $0$. % \begin{macrocode} \cs_new:Npn \@@_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4 { \if_int_compare:w \c_nine < 1 \token_to_str:N #4 \exp_stop_f: \exp_after:wN \@@_parse_pack_trailing:NNNNNNww \exp_after:wN \c_eight \int_use:N \__int_eval:w 1 #1 \token_to_str:N #4 \exp_after:wN \@@_parse_large_round:NN \exp_after:wN #4 \tex_romannumeral:D \else: \exp_after:wN \@@_parse_pack_trailing:NNNNNNww \int_use:N \__int_eval:w \c_seven - #3 \exp_stop_f: \int_use:N \__int_eval:w 1 #1 \if:w . #4 \exp_after:wN \@@_parse_small_trailing:wwNN \__int_value:w 1 \cs:w @@_parse_digits_ \tex_romannumeral:D #3 :N \exp_after:wN \cs_end: \tex_romannumeral:D \else: #2 0 \@@_parse_exponent:Nw #4 \fi: \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \subsubsection{Finding the exponent} % % Expansion is a little bit tricky here, in part because we accept input % where multiplication is implicit. % \begin{verbatim} % \@@_parse:n { 3.2 erf(0.1) } % \@@_parse:n { 3.2 e\l_my_int } % \@@_parse:n { 3.2 \c_pi_fp } % \end{verbatim} % The first case indicates that just looking one character ahead for an % \enquote{\texttt{e}} is not enough, since we would mistake the % function \texttt{erf} for an exponent of \enquote{\texttt{rf}}. An % alternative would be to look two tokens ahead and check if what % follows is a sign or a digit, considering in that case that we must be % finding an exponent. But taking care of the second case requires that % we unpack registers after \texttt{e}. However, blindly expanding the % two tokens ahead completely would break the third example (unpacking % is even worse). Indeed, in the course of reading $3.2$, \cs{c_pi_fp} % is expanded to \cs{s_@@} \cs{@@_chk:w} |1| |0| |{-1}| |{3141}| % $\cdots$ |;| and \cs{s_@@} stops the expansion. Expanding two tokens % ahead would then force the expansion of \cs{@@_chk:w} (despite it % being protected), and that function tries to produce an error. % % What can we do? Really, the reason why this last case breaks is that % just as \TeX{} does, we should read ahead as little as possible. % Here, the only case where there may be an exponent is if the first % token ahead is |e|. Then we expand (and possibly unpack) the second % token --- and hopefully that is safe. % % \begin{macro}[aux, rEXP]{\@@_parse_exponent:Nw} % This auxiliary is convenient to smuggle some material through % \cs{fi:} ending conditional processing. We place those \cs{fi:} % (argument |#2|) at a very odd place becase this allows us to insert % \cs{__int_eval:w} \ldots{} there if needed. % \begin{macrocode} \cs_new:Npn \@@_parse_exponent:Nw #1 #2 \@@_parse_expand:w { \exp_after:wN ; \__int_value:w #2 \@@_parse_exponent:N #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent:N, \@@_parse_exponent_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{macrocode} \cs_new:Npn \@@_parse_exponent:N #1 { \if:w e #1 \exp_after:wN \@@_parse_exponent_aux:N \tex_romannumeral:D \else: 0 \@@_parse_return_semicolon:w #1 \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_exponent_aux:N #1 { \if_int_compare:w \if_catcode:w \tex_relax:D #1 \c_zero \else: `#1 \fi: > `9 \exp_stop_f: 0 \exp_after:wN ; \exp_after:wN e \else: \exp_after:wN \@@_parse_exponent_sign:N \fi: #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent_sign:N} % Read signs one by one (if there is any). % \begin{macrocode} \cs_new:Npn \@@_parse_exponent_sign:N #1 { \if:w + \if:w - #1 + \fi: \token_to_str:N #1 \exp_after:wN \@@_parse_exponent_sign:N \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN \@@_parse_exponent_body:N \exp_after:wN #1 \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent_body:N} % An exponent can be an explicit integer (most common case), or % various other things (most of which are invalid). % \begin{macrocode} \cs_new:Npn \@@_parse_exponent_body:N #1 { \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: \token_to_str:N #1 \exp_after:wN \@@_parse_exponent_digits:N \tex_romannumeral:D \else: \@@_parse_exponent_keep:NTF #1 { \@@_parse_return_semicolon:w #1 } { \exp_after:wN ; \tex_romannumeral:D } \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent_digits:N} % Read digits one by one, and leave them behind in the input stream. % When finding a non-digit, stop, and insert a semicolon. Note that % we don't check for overflow of the exponent, hence there can be a % TeX error. It is mostly harmless, except when parsing % |0e9876543210|, which should be a valid representation of $0$, but % is not. % \begin{macrocode} \cs_new:Npn \@@_parse_exponent_digits:N #1 { \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: \token_to_str:N #1 \exp_after:wN \@@_parse_exponent_digits:N \tex_romannumeral:D \else: \@@_parse_return_semicolon:w #1 \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent_keep:NTF} % This is the last building block for parsing exponents. The argument % |#1| is already fully expanded, and neither |+| nor |-| nor a digit. % It can be: % \begin{itemize} % \item \cs{s_@@}, marking the start of an internal floating point, % invalid here; % \item another control sequence equal to \tn{relax}, probably a bad % variable; % \item a register: in this case we make sure that it is an integer % register, not a dimension; % \item a character other than |+|, |-| or digits, again, an error. % \end{itemize} % \begin{macrocode} \prg_new_conditional:Npnn \@@_parse_exponent_keep:N #1 { TF } { \if_catcode:w \tex_relax:D #1 \if_meaning:w \tex_relax:D #1 \if_int_compare:w \pdftex_strcmp:D { \s_@@ } { #1 } = \c_zero 0 \__msg_kernel_expandable_error:nnn { kernel } { fp-after-e } { floating~point~ } \prg_return_true: \else: 0 \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#1} \prg_return_false: \fi: \else: \if_int_compare:w \pdftex_strcmp:D { \__int_value:w #1 } { \tex_the:D #1 } = \c_zero \__int_value:w #1 \else: 0 \__msg_kernel_expandable_error:nnn { kernel } { fp-after-e } { dimension~#1 } \fi: \prg_return_false: \fi: \else: 0 \__msg_kernel_expandable_error:nnn { kernel } { fp-missing } { exponent } \prg_return_true: \fi: } % \end{macrocode} % \end{macro} % % ^^A begin[todo] % ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead" % % \subsubsection{Beyond 16 digits: rounding} % % \begin{macro}[int]{\@@_cfs_round_loop:N} % Used both for \cs{@@_parse_small_round:NN} and % \cs{@@_parse_large_round:NN}. % Should appear after a \cs{__int_eval:w} |0|. Reads digits one by one, % until reaching a non-digit. Adds |+1| for each digit. If all digits % found are |0|, ends the \cs{__int_eval:w} by |;\c_zero|, otherwise % by |;\c_one|. This is done by switching the loop to |round_up| % at the first non-zero digit. % % \begin{macrocode} \cs_new:Npn \@@_cfs_round_loop:N #1 { \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: + \c_one \if:w 0 #1 \exp_after:wN \@@_cfs_round_loop:N \tex_romannumeral:D \else: \exp_after:wN \@@_cfs_round_up:N \tex_romannumeral:D \fi: \else: \@@_parse_return_semicolon:w \c_zero #1 \fi: \@@_parse_expand:w } \cs_new:Npn \@@_cfs_round_up:N #1 { \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: + 1 \exp_after:wN \@@_cfs_round_up:N \tex_romannumeral:D \else: \@@_parse_return_semicolon:w \c_one #1 \fi: \@@_parse_expand:w } % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\@@_parse_large_round:NN} % \begin{syntax} % \cs{@@_parse_large_round:NN} \meta{digit} \meta{more digits} % \end{syntax} % \meta{digit} is the digit that we are currently rounding (we only % care whether it is even or odd). % % The goal is to get \cs{c_zero} or \cs{c_one}, check for an exponent % afterwards, and combine it to the number of digits before the decimal % point (which we thus need to keep track of). % \begin{macrocode} \cs_new:Npn \@@_parse_large_round:NN #1#2 { \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: + \exp_after:wN \@@_round_s:NNNw \exp_after:wN 0 \exp_after:wN #1 \exp_after:wN #2 \int_use:N \__int_eval:w \exp_after:wN \@@_parse_large_round_after:wNN \int_use:N \__int_eval:w \c_one \exp_after:wN \@@_cfs_round_loop:N \else: %^^A could be dot, or e, or other \exp_after:wN \@@_parse_large_round_dot_test:NNw \exp_after:wN #1 \exp_after:wN #2 \fi: } \cs_new:Npn \@@_parse_large_round_dot_test:NNw #1#2 { \if:w . #2 \exp_after:wN \@@_parse_small_round:NN \exp_after:wN #1 \tex_romannumeral:D \else: \@@_parse_exponent:Nw #2 \fi: \@@_parse_expand:w } % \end{macrocode} % \begin{syntax} % \cs{@@_parse_large_round_after:wNN} \meta{exp} |;| % ~~\meta{0 or 1} \meta{next~token} % \end{syntax} % \begin{macrocode} \cs_new:Npn \@@_parse_large_round_after:wNN #1 ; #2 #3 { \if:w . #3 \exp_after:wN \@@_parse_large_round_after_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 } % \end{macrocode} % \end{macro} % % % % \begin{macro}[int]{\@@_parse_small_round:NN} % \begin{syntax} % \cs{@@_parse_small_round:NN} \meta{digit} \meta{more digits} % \end{syntax} % \meta{digit} is the digit that we are currently rounding (we only % care whether it is even or odd). % % The goal is to get \cs{c_zero} or \cs{c_one} % \begin{macrocode} \cs_new:Npn \@@_parse_small_round:NN #1#2 { \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: + \exp_after:wN \@@_round_s:NNNw \exp_after:wN 0 \exp_after:wN #1 \exp_after:wN #2 \int_use:N \__int_eval:w \exp_after:wN \@@_parse_small_round_after:wN \int_use:N \__int_eval:w \c_zero \exp_after:wN \@@_cfs_round_loop:N \tex_romannumeral:D \else: \@@_parse_exponent:Nw #2 \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_small_round_after:wN #1; #2 { + #2 \exp_after:wN ; \__int_value:w \@@_parse_exponent:N } % \end{macrocode} % \end{macro} % % % \subsection{Main functions} % % \begin{macro}[int, EXP]{\@@_parse:n} % \begin{macro}[aux, EXP]{\@@_parse_after:ww} % Start a \tn{romannumeral} expansion so that \cs{@@_parse:n} expands % in two steps. The \cs{@@_parse_until:Nw} function will perform % computations until reaching an operation with precedence % \cs{c_minus_one} or less. Then check that there was indeed nothing % left (this cannot happen), and stop the initial expansion with % \cs{c_zero}.%^^A todo: simplify a bit. % \begin{macrocode} \cs_new:Npn \@@_parse:n #1 { \tex_romannumeral:D \exp_after:wN \@@_parse_after:ww \tex_romannumeral:D \@@_parse_until:Nw \c_minus_one \@@_parse_expand:w #1 \s_@@_mark \s_@@_stop } \cs_new:Npn \@@_parse_after:ww #1@ #2 \s_@@_stop { % \assert_str_eq:nn { #2 } { \@@_parse_infix_end:N \s_@@_mark } \c_zero #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int, EXP]{\@@_parse_until:Nw} % \begin{macro}[aux, EXP]{\@@_parse_until_test:NwN} % The \cs{@@_parse_until} % This is just a shorthand which sets up both \cs{@@_parse_until_test} % and \cs{@@_parse_operand} with the same precedence. Note the % trailing \cs{tex_romannumeral:D}. This function should be % used with much care. % \begin{macrocode} \cs_new:Npn \@@_parse_until:Nw #1 { -`0 \exp_after:wN \@@_parse_until_test:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \@@_parse_operand:Nw \exp_after:wN #1 \tex_romannumeral:D } \cs_new:Npn \@@_parse_until_test:NwN #1 #2 @ #3 { #3 #1 #2 @ } \cs_new_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{macrocode} \cs_new:Npn \@@_parse_infix_after_operand:NwN #1 #2; { \@@_exp_after_f:nw { \@@_parse_infix:NN #1 } #2; } \group_begin: \char_set_catcode_letter:N \* \cs_new:Npn \@@_parse_infix:NN #1 #2 { \if_catcode:w \tex_relax:D #2 \if_int_compare:w \pdftex_strcmp:D { \s_@@_mark } { #2 } = \c_zero \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_infix_end:N \else: \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_infix_juxtapose:N \fi: \else: \if_int_compare:w \__int_eval:w \tex_uccode:D `#2 / 26 = \c_three \exp_after:wN \exp_after:wN \exp_after:wN \@@_parse_infix_juxtapose:N \else: \exp_after:wN \@@_parse_infix_check:NNN \cs:w @@_parse_infix_#2:N \exp_after:wN \exp_after:wN \exp_after:wN \cs_end: \fi: \fi: #1 #2 } \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3 { \if_meaning:w \tex_relax:D #1 \__msg_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, 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} % % \begin{macro}[aux, EXP] % { % \@@_parse_word_inf:N, \@@_parse_word_nan:N, \@@_parse_word_pi:N , % \@@_parse_word_deg:N, \@@_parse_word_em:N , % \@@_parse_word_ex:N , \@@_parse_word_in:N , \@@_parse_word_pt:N , % \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N , % \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N , % \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N , % \@@_parse_word_true:N , \@@_parse_word_false:N , % } % A whole bunch of floating point numbers. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 #2 { \cs_new_nopar:cpn { @@_parse_word_#1:N } { \exp_after:wN #2 \tex_romannumeral:D -`0 \@@_parse_infix:NN } } \@@_tmp:w { inf } \c_inf_fp \@@_tmp:w { nan } \c_nan_fp \@@_tmp:w { pi } \c_pi_fp \@@_tmp:w { deg } \c_one_degree_fp \@@_tmp:w { true } \c_one_fp \@@_tmp:w { false } \c_zero_fp \@@_tmp:w { pt } \c_one_fp \cs_set_protected:Npn \@@_tmp:w #1 #2 { \cs_new_nopar:cpn { @@_parse_word_#1:N } { \@@_exp_after_f:nw { \@@_parse_infix:NN } \s_@@ \@@_chk:w 10 #2 ; } } \@@_tmp:w {in} { {2} {7227} {0000} {0000} {0000} } \@@_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} } \@@_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} } \@@_tmp:w {mm} { {1} {2845} {2755} {9055} {1181} } \@@_tmp:w {dd} { {1} {1070} {0085} {6496} {0630} } \@@_tmp:w {cc} { {2} {1284} {0102} {7795} {2756} } \@@_tmp:w {nd} { {1} {1066} {9783} {4645} {6693} } \@@_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} } \@@_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} } \@@_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} } \tl_map_inline:nn { {em} {ex} } { \cs_new_nopar:cpn { @@_parse_word_#1:N } { \exp_after:wN \dim_to_fp:n \exp_after:wN { \dim_use:N \__dim_eval:w 1 #1 \exp_after:wN } \tex_romannumeral:D -`0 \@@_parse_infix:NN } } % \end{macrocode} % \end{macro} % % \begin{macro}[int, EXP] % { % \@@_parse_word_abs:N , % \@@_parse_word_cos:N , % \@@_parse_word_cot:N , % \@@_parse_word_csc:N , % \@@_parse_word_exp:N , % \@@_parse_word_ln:N , % \@@_parse_word_sec:N , % \@@_parse_word_sin:N , % \@@_parse_word_tan:N , % } % Unary functions, which are applied to all of their arguments when % receiving an array. % \begin{macrocode} \tl_map_inline:nn { {abs} {cos} {cot} {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 } } % \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 } } \@@_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} % This function expects one or two arguments. % \begin{macrocode} \cs_new:Npn \@@_parse_word_round:N #1#2 { \if_meaning:w + #2 \@@_parse_round:Nw \@@_round_to_pinf:NNN \else: \if_meaning:w 0 #2 \@@_parse_round:Nw \@@_round_to_zero:NNN \else: \if_meaning:w - #2 \@@_parse_round:Nw \@@_round_to_ninf:NNN \fi: \fi: \fi: \exp_after:wN \@@_parse_apply_round:NNwN \exp_after:wN #1 \exp_after:wN \@@_round_to_nearest:NNN \tex_romannumeral:D \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w #2 } \cs_new:Npn \@@_parse_round:Nw #1 #2 \@@_round_to_nearest:NNN #3 \@@_parse_expand:w #4 { #2 #1 #3 \@@_parse_expand:w } \cs_new:Npn \@@_parse_apply_round:NNwN #1#2#3@#4 { \if_case:w \__int_eval:w \@@_array_count:n {#3} - \c_one \__int_eval_end: \@@_round:Nwn #2 #3 {0} \tex_romannumeral:D \or: \@@_round:Nww #2 #3 \tex_romannumeral:D \else: \__msg_kernel_expandable_error:nnnnn { kernel } { fp-num-args } { round() } { 1 } { 2 } \exp_after:wN \c_nan_fp \tex_romannumeral:D \fi: -`0 #4 #1 } % \end{macrocode} % \end{macro} % % \subsubsection{Unary minus, plus, not} % % \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw} % A unary |+| does nothing. % \begin{macrocode} \cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_operand:Nw % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw} % Unary |-| is harder. % Boolean not. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1#2 { \cs_new:cpn { @@_parse_prefix_#1:Nw } ##1 { \exp_after:wN \@@_parse_apply_unary:NNwN \exp_after:wN ##1 \cs:w @@_ #2 \exp_after:wN \@@_parse_unary_type:N \tex_romannumeral:D \if_int_compare:w \c_twelve < ##1 \@@_parse_until:Nw ##1 \else: \@@_parse_until:Nw \c_twelve \fi: \@@_parse_expand:w } } \@@_tmp:w - { - } \@@_tmp:w ! { ! } % \end{macrocode} % \end{macro} % % \subsubsection{Other prefixes} % % \begin{macro}[int]{\@@_parse_prefix_(:Nw} % \begin{macrocode} \group_begin: \char_set_catcode_letter:N \) \cs_new:cpn { @@_parse_prefix_(:Nw } #1 { \exp_after:wN \@@_parse_lparen_after:NwN \exp_after:wN #1 \tex_romannumeral:D \if_int_compare:w #1 = \c_sixteen \@@_parse_until:Nw \c_one \else: \@@_parse_until:Nw \c_zero \fi: \@@_parse_expand:w } \cs_new:Npn \@@_parse_lparen_after:NwN #1#2@#3 { \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N { \@@_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 } } \group_end: % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_parse_prefix_.:Nw} % This function is called when a number starts with a dot. % \begin{macrocode} \cs_new:cpn {@@_parse_prefix_.:Nw} #1 { \exp_after:wN \@@_parse_infix_after_operand:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \@@_sanitize:wN \int_use:N \__int_eval:w \c_zero \@@_parse_strim_zeros:N } % \end{macrocode} % \end{macro} % % \subsection{Infix operators} % % As described in the \enquote{work plan}, each infix operator has an % associated \cs{infix} function, a computing function, and % precedence, given as arguments to \cs{@@_tmp:w}. The % latter two are only needed when defining the \cs{infix} function. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1#2#3#4 { \cs_new:Npn #1 ##1 { \if_int_compare:w ##1 < #3 \exp_after:wN @ \exp_after:wN \@@_parse_apply_binary:NwNwN \exp_after:wN #2 \tex_romannumeral:D \@@_parse_until:Nw #4 \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN @ \exp_after:wN \@@_parse_stop_until:N \exp_after:wN #1 \fi: } } % \end{macrocode} % % \begin{macro}[int, EXP] % { % \@@_parse_infix_+:N, \@@_parse_infix_-:N, % \@@_parse_infix_/:N, \@@_parse_infix_mul:N, % \@@_parse_infix_and:N, \@@_parse_infix_or:N, % } % Using the general mechanism for arithmetic operations. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \& \@@_tmp:w \@@_parse_infix_juxtapose:N * \c_thirty_two \c_thirty_two \exp_args:Nc \@@_tmp:w { @@_parse_infix_ / :N } / \c_ten \c_ten \exp_args:Nc \@@_tmp:w { @@_parse_infix_mul:N } * \c_ten \c_ten \exp_args:Nc \@@_tmp:w { @@_parse_infix_ - :N } - \c_nine \c_nine \exp_args:Nc \@@_tmp:w { @@_parse_infix_ + :N } + \c_nine \c_nine \exp_args:Nc \@@_tmp:w { @@_parse_infix_and:N } & \c_five \c_five \exp_args:Nc \@@_tmp:w { @@_parse_infix_ or:N } | \c_four \c_four \group_end: % \end{macrocode} % \end{macro} % % \begin{macro}[int, EXP]{\@@_parse_infix_*:N} % \begin{macro}[int, EXP]+\@@_parse_infix_^:N+ % The power operation must be associative in the opposite order from % all others. For this, we reverse the test, hence treating a % \enquote{previous precedence} of \cs{c_fourteen} as less binding % than |^|. % \begin{macrocode} \group_begin: \char_set_catcode_letter:N ^ \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen \cs_new:cpn { @@_parse_infix_*:N } #1#2 { \if:w * #2 \exp_after:wN \@@_parse_infix_^:N \exp_after:wN #1 \else: \exp_after:wN \@@_parse_infix_mul:N \exp_after:wN #1 \exp_after:wN #2 \fi: } \group_end: % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int, EXP]+\@@_parse_infix_|:Nw+ % \begin{macro}[int, EXP]+\@@_parse_infix_&:Nw+ % \begin{macrocode} \group_begin: \char_set_catcode_letter:N \| \char_set_catcode_letter:N \& \cs_new:Npn \@@_parse_infix_|:N #1#2 { \if:w | #2 \exp_after:wN \@@_parse_infix_|:N \exp_after:wN #1 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN \@@_parse_infix_or:N \exp_after:wN #1 \exp_after:wN #2 \fi: } \cs_new:Npn \@@_parse_infix_&:N #1#2 { \if:w & #2 \exp_after:wN \@@_parse_infix_&:N \exp_after:wN #1 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN \@@_parse_infix_and:N \exp_after:wN #1 \exp_after:wN #2 \fi: } \group_end: % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int, EXP] % { % \@@_parse_infix_<:N, \@@_parse_infix_=:N, % \@@_parse_infix_>:N, \@@_parse_infix_!:N % } % \begin{macro}[aux, EXP] % { % \@@_parse_infix_excl_aux:NN, % \@@_parse_infix_excl_error:, % \@@_infix_compare:N, % \@@_parse_compare:NNNNNNw, % \@@_parse_compare_expand:NNNNNw, % \@@_parse_compare_end:NNNN, % \@@_compare:wNNNNw, % } % \begin{macrocode} \cs_new:cpn { @@_parse_infix_<:N } #1 { \@@_infix_compare:N #1 \c_one_fp \c_zero_fp \c_zero_fp \c_zero_fp \c_zero_fp < } \cs_new:cpn { @@_parse_infix_=:N } #1 { \@@_infix_compare:N #1 \c_one_fp \c_zero_fp \c_zero_fp \c_zero_fp \c_zero_fp = } \cs_new:cpn { @@_parse_infix_>:N } #1 { \@@_infix_compare:N #1 \c_one_fp \c_zero_fp \c_zero_fp \c_zero_fp \c_zero_fp > } \cs_new:cpn { @@_parse_infix_!:N } #1 { \exp_after:wN \@@_parse_infix_excl_aux:NN \exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w } \cs_new:Npn \@@_parse_infix_excl_aux:NN #1#2 { \@@_infix_compare:N #1 \c_zero_fp \c_one_fp \c_one_fp \c_one_fp \c_one_fp #2 } \cs_new:Npn \@@_parse_infix_excl_error: { \__msg_kernel_expandable_error:nnnn { kernel } { fp-missing } { = } { ~after~!. } } \cs_new:Npn \@@_infix_compare:N #1 { \if_int_compare:w #1 < \c_seven \exp_after:wN \@@_parse_compare:NNNNNNw \exp_after:wN \@@_parse_infix_excl_error: \else: \exp_after:wN @ \exp_after:wN \@@_parse_stop_until: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 \c_minus_one \else: \__int_eval:w `#7 - `< \__int_eval_end: \fi: \@@_parse_compare_expand:NNNNNw #2#2#4#5#6 \or: \@@_parse_compare_expand:NNNNNw #2#3#2#5#6 \or: \@@_parse_compare_expand:NNNNNw #2#3#4#2#6 \or: \@@_parse_compare_expand:NNNNNw #2#3#4#5#2 \else: #1 \@@_parse_compare_end:NNNN #3#4#5#6#7 \fi: } \cs_new:Npn \@@_parse_compare_expand:NNNNNw #1#2#3#4#5 { \exp_after:wN \@@_parse_compare:NNNNNNw \exp_after:wN \prg_do_nothing: \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \exp_after:wN #4 \exp_after:wN #5 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w } \cs_new:Npn \@@_parse_compare_end:NNNN #1#2#3#4#5 \fi: { \fi: \exp_after:wN @ \exp_after:wN \@@_parse_apply_compare:NwNNNNwN \exp_after:wN #1 \exp_after:wN #2 \exp_after:wN #3 \exp_after:wN #4 \tex_romannumeral:D \@@_parse_until:Nw \c_seven \@@_parse_expand:w #5 } \cs_new:Npn \@@_parse_apply_compare:NwNNNNwN #1 #2@ #3#4#5#6 #7@ #8 { \exp_after:wN \@@_parse_until_test:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN \if_case:w \@@_compare_back:ww #7 #2 \exp_stop_f: #4 \or: #5 \or: #6 \else: #3 \fi: \tex_romannumeral:D -`0 #8 #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N} % \begin{macrocode} \group_begin: \char_set_catcode_letter:N \? \cs_new:Npn \@@_parse_infix_?:N #1 { \if_int_compare:w #1 < \c_three \exp_after:wN @ \exp_after:wN \@@_ternary:NwwN \tex_romannumeral:D \@@_parse_until:Nw \c_three \exp_after:wN \@@_parse_expand:w \else: \exp_after:wN @ \exp_after:wN \@@_parse_stop_until:N \exp_after:wN \@@_parse_infix_?:N \fi: } \cs_new:Npn \@@_parse_infix_::N #1 { \if_int_compare:w #1 < \c_three \__msg_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} % % \section{Messages} % % \begin{macrocode} \__msg_kernel_new:nnn { kernel } { unknown-fp-word } { Unknown~fp~word~#1. } \__msg_kernel_new:nnn { kernel } { fp-missing } { Missing~#1~inserted #2. } \__msg_kernel_new:nnn { kernel } { fp-extra } { Extra~#1~ignored. } \__msg_kernel_new:nnn { kernel } { fp-early-end } { Premature~end~in~fp~expression. } \__msg_kernel_new:nnn { kernel } { fp-after-e } { Cannot~use~#1 after~'e'. } \__msg_kernel_new:nnn { kernel } { fp-missing-number } { Missing~number~before~'#1'. } \__msg_kernel_new:nnn { kernel } { fp-unknown-symbol } { Unknown~symbol~#1~ignored. } \__msg_kernel_new:nnn { kernel } { fp-extra-comma } { Unexpected~comma:~extra~arguments~ignored. } \__msg_kernel_new:nnn { kernel } { fp-num-args } { #1~expects~between~#2~and~#3~arguments. } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintChanges % % \PrintIndex