summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3fp-traps.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3fp-traps.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-traps.dtx400
1 files changed, 400 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/l3fp-traps.dtx b/macros/latex/contrib/l3kernel/l3fp-traps.dtx
new file mode 100644
index 0000000000..aad063f831
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3fp-traps.dtx
@@ -0,0 +1,400 @@
+% \iffalse meta-comment
+%
+%% File: l3fp-traps.dtx
+%
+% Copyright (C) 2011-2014,2016-2019 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
+%
+% https://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 development version of the bundle can be found at
+%
+% https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full,kernel]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{The \textsf{l3fp-traps} package\\
+% Trapping floating-point exceptions}
+% \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 2019-08-25}
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-traps} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% Exceptions should be accessed by an \texttt{n}-type argument, among
+% \begin{itemize}
+% \item \texttt{invalid_operation}
+% \item \texttt{division_by_zero}
+% \item \texttt{overflow}
+% \item \texttt{underflow}
+% \item \texttt{inexact} (actually never used).
+% \end{itemize}
+%
+% \subsection{Flags}
+%
+% \begin{variable}
+% {
+% flag fp_invalid_operation,
+% flag fp_division_by_zero,
+% flag fp_overflow,
+% flag fp_underflow
+% }
+% Flags to denote exceptions.
+% \begin{macrocode}
+\flag_new:n { fp_invalid_operation }
+\flag_new:n { fp_division_by_zero }
+\flag_new:n { fp_overflow }
+\flag_new:n { fp_underflow }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Traps}
+%
+% Exceptions can be trapped to obtain custom behaviour. When an invalid
+% operation or a division by zero is trapped, the trap receives as
+% arguments the result as an |N|-type floating point number, the
+% function name (multiple letters for prefix operations, or a single
+% symbol for infix operations), and the operand(s). When an overflow or
+% underflow is trapped, the trap receives the resulting overly large or
+% small floating point number if it is not too big, otherwise it
+% receives $+\infty$. Currently, the inexact exception is entirely
+% ignored.
+%
+% The behaviour when an exception occurs is controlled by the
+% definitions of the functions
+% \begin{itemize}
+% \item \cs{@@_invalid_operation:nnw},
+% \item \cs{@@_invalid_operation_o:Nww},
+% \item \cs{@@_invalid_operation_tl_o:ff},
+% \item \cs{@@_division_by_zero_o:Nnw},
+% \item \cs{@@_division_by_zero_o:NNww},
+% \item \cs{@@_overflow:w},
+% \item \cs{@@_underflow:w}.
+% \end{itemize}
+% Rather than changing them directly, we provide a user interface as
+% \cs{fp_trap:nn} \Arg{exception} \Arg{way of trapping}, where the
+% \meta{way of trapping} is one of \texttt{error}, \texttt{flag}, or
+% \texttt{none}.
+%
+% We also provide \cs{@@_invalid_operation_o:nw}, defined in terms of
+% \cs{@@_invalid_operation:nnw}.
+%
+% \begin{macro}{\fp_trap:nn}
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_trap:nn #1#2
+ {
+ \cs_if_exist_use:cF { @@_trap_#1_set_#2: }
+ {
+ \clist_if_in:nnTF
+ { invalid_operation , division_by_zero , overflow , underflow }
+ {#1}
+ {
+ \__kernel_msg_error:nnxx { kernel }
+ { unknown-fpu-trap-type } {#1} {#2}
+ }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { unknown-fpu-exception } {#1}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_invalid_operation_set_error: ,
+% \@@_trap_invalid_operation_set_flag: ,
+% \@@_trap_invalid_operation_set_none: ,
+% \@@_trap_invalid_operation_set:N ,
+% }
+% We provide three types of trapping for invalid operations: either
+% produce an error and raise the relevant flag; or only raise the
+% flag; or don't even raise the flag. In most cases, the function
+% produces as a result its first argument, possibly with
+% post-expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_error:
+ { \@@_trap_invalid_operation_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_flag:
+ { \@@_trap_invalid_operation_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set_none:
+ { \@@_trap_invalid_operation_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_invalid_operation_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation:nnw ##1##2##3; }
+ {
+ #1
+ \@@_error:nnfn { fp-invalid } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_o:Nww ##1##2; ##3; }
+ {
+ #1
+ \@@_error:nffn { fp-invalid-ii }
+ { \fp_to_tl:n { ##2; } } { \fp_to_tl:n { ##3; } } {##1}
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ \exp_after:wN \c_nan_fp
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_invalid_operation_tl_o:ff ##1##2 }
+ {
+ #1
+ \@@_error:nffn { fp-invalid } {##1} {##2} { }
+ \flag_raise_if_clear:n { fp_invalid_operation }
+ \exp_after:wN \c_nan_fp
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_division_by_zero_set_error: ,
+% \@@_trap_division_by_zero_set_flag: ,
+% \@@_trap_division_by_zero_set_none: ,
+% \@@_trap_division_by_zero_set:N ,
+% }
+% We provide three types of trapping for invalid operations and
+% division by zero: either produce an error and raise the relevant
+% flag; or only raise the flag; or don't even raise the flag. In all
+% cases, the function must produce a result, namely its first
+% argument, $\pm\infty$ or \nan{}.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_error:
+ { \@@_trap_division_by_zero_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_flag:
+ { \@@_trap_division_by_zero_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set_none:
+ { \@@_trap_division_by_zero_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_division_by_zero_set:N #1
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_division_by_zero_o:Nnw ##1##2##3; }
+ {
+ #1
+ \@@_error:nnfn { fp-zero-div } {##2} { \fp_to_tl:n { ##3; } } { }
+ \flag_raise_if_clear:n { fp_division_by_zero }
+ \exp_after:wN ##1
+ }
+ \exp_args:Nno \use:n
+ { \cs_set:Npn \@@_division_by_zero_o:NNww ##1##2##3; ##4; }
+ {
+ #1
+ \@@_error:nffn { fp-zero-div-ii }
+ { \fp_to_tl:n { ##3; } } { \fp_to_tl:n { ##4; } } {##2}
+ \flag_raise_if_clear:n { fp_division_by_zero }
+ \exp_after:wN ##1
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_trap_overflow_set_error: ,
+% \@@_trap_overflow_set_flag: ,
+% \@@_trap_overflow_set_none: ,
+% \@@_trap_overflow_set:N ,
+% }
+% \begin{macro}
+% {
+% \@@_trap_underflow_set_error: ,
+% \@@_trap_underflow_set_flag: ,
+% \@@_trap_underflow_set_none: ,
+% \@@_trap_underflow_set:N ,
+% }
+% \begin{macro}{\@@_trap_overflow_set:NnNn}
+% Just as for invalid operations and division by zero, the three
+% different behaviours are obtained by feeding \cs{prg_do_nothing:},
+% \cs{use_none:nnnnn} or \cs{use_none:nnnnnnn} to an auxiliary, with a
+% further auxiliary common to overflow and underflow functions. In
+% most cases, the argument of the \cs{@@_overflow:w} and
+% \cs{@@_underflow:w} functions will be an (almost) normal number
+% (with an exponent outside the allowed range), and the error message
+% thus displays that number together with the result to which it
+% overflowed or underflowed. For extreme cases such as \texttt{10 **
+% 1e9999}, the exponent would be too large for \TeX{}, and
+% \cs{@@_overflow:w} receives $\pm \infty$ (\cs{@@_underflow:w} would
+% receive $\pm 0$); then we cannot do better than simply say an
+% overflow or underflow occurred.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_trap_overflow_set_error:
+ { \@@_trap_overflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_overflow_set_flag:
+ { \@@_trap_overflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_overflow_set_none:
+ { \@@_trap_overflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_overflow_set:N #1
+ { \@@_trap_overflow_set:NnNn #1 { overflow } \@@_inf_fp:N { inf } }
+\cs_new_protected:Npn \@@_trap_underflow_set_error:
+ { \@@_trap_underflow_set:N \prg_do_nothing: }
+\cs_new_protected:Npn \@@_trap_underflow_set_flag:
+ { \@@_trap_underflow_set:N \use_none:nnnnn }
+\cs_new_protected:Npn \@@_trap_underflow_set_none:
+ { \@@_trap_underflow_set:N \use_none:nnnnnnn }
+\cs_new_protected:Npn \@@_trap_underflow_set:N #1
+ { \@@_trap_overflow_set:NnNn #1 { underflow } \@@_zero_fp:N { 0 } }
+\cs_new_protected:Npn \@@_trap_overflow_set:NnNn #1#2#3#4
+ {
+ \exp_args:Nno \use:n
+ { \cs_set:cpn { @@_ #2 :w } \s_@@ \@@_chk:w ##1##2##3; }
+ {
+ #1
+ \@@_error:nffn
+ { fp-flow \if_meaning:w 1 ##1 -to \fi: }
+ { \fp_to_tl:n { \s_@@ \@@_chk:w ##1##2##3; } }
+ { \token_if_eq_meaning:NNF 0 ##2 { - } #4 }
+ {#2}
+ \flag_raise_if_clear:n { fp_#2 }
+ #3 ##2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_invalid_operation:nnw, \@@_invalid_operation_o:Nww,
+% \@@_invalid_operation_tl_o:ff,
+% \@@_division_by_zero_o:Nnw, \@@_division_by_zero_o:NNww,
+% \@@_overflow:w , \@@_underflow:w
+% }
+% Initialize the control sequences (to log properly their
+% existence). Then set invalid operations to trigger an error, and
+% division by zero, overflow, and underflow to act silently on their
+% flag.
+% \begin{macrocode}
+\cs_new:Npn \@@_invalid_operation:nnw #1#2#3; { }
+\cs_new:Npn \@@_invalid_operation_o:Nww #1#2; #3; { }
+\cs_new:Npn \@@_invalid_operation_tl_o:ff #1 #2 { }
+\cs_new:Npn \@@_division_by_zero_o:Nnw #1#2#3; { }
+\cs_new:Npn \@@_division_by_zero_o:NNww #1#2#3; #4; { }
+\cs_new:Npn \@@_overflow:w { }
+\cs_new:Npn \@@_underflow:w { }
+\fp_trap:nn { invalid_operation } { error }
+\fp_trap:nn { division_by_zero } { flag }
+\fp_trap:nn { overflow } { flag }
+\fp_trap:nn { underflow } { flag }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\@@_invalid_operation_o:nw, \@@_invalid_operation_o:fw}
+% Convenient short-hands for returning \cs{c_nan_fp} for a unary or
+% binary operation, and expanding after.
+% \begin{macrocode}
+\cs_new:Npn \@@_invalid_operation_o:nw
+ { \@@_invalid_operation:nnw { \exp_after:wN \c_nan_fp } }
+\cs_generate_variant:Nn \@@_invalid_operation_o:nw { f }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Errors}
+%
+% \begin{macro}[EXP]{\@@_error:nnnn, \@@_error:nnfn, \@@_error:nffn, \@@_error:nfff}
+% \begin{macrocode}
+\cs_new:Npn \@@_error:nnnn
+ { \__kernel_msg_expandable_error:nnnnn { kernel } }
+\cs_generate_variant:Nn \@@_error:nnnn { nnf, nff , nfff }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+% Some messages.
+% \begin{macrocode}
+\__kernel_msg_new:nnnn { kernel } { unknown-fpu-exception }
+ {
+ The~FPU~exception~'#1'~is~not~known:~
+ that~trap~will~never~be~triggered.
+ }
+ {
+ The~only~exceptions~to~which~traps~can~be~attached~are \\
+ \iow_indent:n
+ {
+ * ~ invalid_operation \\
+ * ~ division_by_zero \\
+ * ~ overflow \\
+ * ~ underflow
+ }
+ }
+\__kernel_msg_new:nnnn { kernel } { unknown-fpu-trap-type }
+ { The~FPU~trap~type~'#2'~is~not~known. }
+ {
+ The~trap~type~must~be~one~of \\
+ \iow_indent:n
+ {
+ * ~ error \\
+ * ~ flag \\
+ * ~ none
+ }
+ }
+\__kernel_msg_new:nnn { kernel } { fp-flow }
+ { An ~ #3 ~ occurred. }
+\__kernel_msg_new:nnn { kernel } { fp-flow-to }
+ { #1 ~ #3 ed ~ to ~ #2 . }
+\__kernel_msg_new:nnn { kernel } { fp-zero-div }
+ { Division~by~zero~in~ #1 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-zero-div-ii }
+ { Division~by~zero~in~ (#1) #3 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-invalid }
+ { Invalid~operation~ #1 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-invalid-ii }
+ { Invalid~operation~ (#1) #3 (#2) }
+\__kernel_msg_new:nnn { kernel } { fp-unknown-type }
+ { Unknown~type~for~'#1' }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex