Is there a command that asks whether an argument to \fp_eval is a parse error or not? E.g.:
\fp_if_valid:nTF { 1+2.3*2 } { yes } { no } % yes
\fp_if_valid:nTF { 1+2.3* } { yes } { no } % no
I would like to be able to sanitize user input and throw my own error if their input is not valid. Anyways I was just curious whether there is a built-in command to do this, or whether the latex3 team intends to add this functionality. I wrote the following code, which seems to work (thanks to Rob Hall for fixing some mistakes):
\documentclass{article}
\usepackage{xfp}
\ExplSyntaxOn
% Add missing variant based on definition of \exp_last_unbraced:Nf in l3expan.dtx
\cs_set:Npn \exp_last_unbraced:NNf #1#2#3
{ \exp_after:wN #1 \exp_after:wN #2 \exp:w \exp_end_continue_f:w #3 }
\tl_new:N \l_my_fp_if_valid_tl
% \return_marker: for maintaining true/false state outside \fp_eval program flow
\scan_new:N \my_fp_if_valid_return_marker:
% Protected wrappers for \prg_return_true: and \prg_return_false:
\cs_new_protected:Nn \my_fp_if_valid_return_true: { \prg_return_true: }
\cs_new_protected:Nn \my_fp_if_valid_return_false: { \prg_return_false: }
% Replace "\return_marker: \return_<true or false>:"
% with "\return_marker: \return_false:"
\cs_new:Npn \my_fp_if_valid_set_return_false:wN #1 \my_fp_if_valid_return_marker: #2
{ #1 \my_fp_if_valid_return_marker: \my_fp_if_valid_return_false: }
\prg_new_protected_conditional:Nnn \my_fp_if_valid:n {T, F, TF} {
\group_begin:
% make error command throw away error message and call \set_return_false:wN
\cs_set:Nn \__msg_expandable_error:n { \my_fp_if_valid_set_return_false:wN }
% Sets \l_if_valid_tl to
% "<numerical result> \return_marker: \return_<true or false>:"
\tl_set:Nx \l_my_fp_if_valid_tl {
\fp_eval:n { #1 }
\my_fp_if_valid_return_marker: \my_fp_if_valid_return_true:
}
% Put <numerical result> into \result: and evaluate \return_<true or false>:
\exp_last_unbraced:NNf \group_end:
\my_fp_if_valid_helper:w \tl_use:N \l_my_fp_if_valid_tl
}
% Put <numerical result> into \result:
\cs_new:Npn \my_fp_if_valid_helper:w #1 \my_fp_if_valid_return_marker: {
\cs_set:Nn \my_fp_if_valid_result: { #1 }
}
\cs_new_eq:NN \fpifvalid \my_fp_if_valid:nTF
\ExplSyntaxOff
\usepackage{xcolor}
\def\testfpifvalid#1{\fpifvalid{#1}{\color{blue}}{\color{red}}\texttt{#1}\par}
\begin{document}
\testfpifvalid{1+1} % valid
\testfpifvalid{1+} % invalid
\testfpifvalid{1+2.3*2} % valid
\testfpifvalid{1+2.3 2} % valid
\testfpifvalid{1+2.3.*2} % invalid
\testfpifvalid{1++2} % valid
\testfpifvalid{1+*2} % invalid
\testfpifvalid{1*+2} % valid
\testfpifvalid{1/0} % invalid
\testfpifvalid{1)2} % invalid
\testfpifvalid{1(2} % invalid
\testfpifvalid{1(2)} % valid
\testfpifvalid{1+1(2/6)+sin(7)} % valid
\testfpifvalid{floor(6,-1)} % valid
\testfpifvalid{floor(6,7,7)} % invalid
\end{document}
xparseto provide the document-level interface. – cfr Feb 25 '18 at 20:57my_prefix to everything. – Hood Chatham Feb 25 '18 at 21:07xparseinstead of performing a\let(or maybe a\defwrapper, in case the definition of the inner command might change). The point ofxparseis to handle complicated argument syntax. If my argument syntax is just that I want a single manditory short argument, what's the benefit? – Hood Chatham Feb 25 '18 at 21:08\cs_new_eqwith a function name not in line with the naming. But you are handling input: you are providing\fpifvalidwhich takes 3 arguments. The point ofxparse, as I understand it, is to provide document-level macros (at least in cases which one or more arguments are or may be involved). That is, it is not just for more complicated cases, it is for simple and complicated cases which expose expl3 functions at the document-level. (& of course there are good reasons to avoid\let,\def, though\letsometimes seems most suitable for variables?) – cfr Feb 25 '18 at 21:29\fpfifvalidthat takes three mandatory arguments, the first short, the other two long.\fp_if_valid:nTFtakes three mandatory arguments, the first short, the other two long. Why do I need a fancy package for this? Also,\cs_new_eq:NNis just a variant of\letthat does an existence safety check, and\cs_new:Npnis a version of\defthat does an existence check. – Hood Chatham Feb 27 '18 at 01:55expl3is for programming (and the naming conventions should be followed),xparseis for document commands (and those naming conventions do not apply). (This is similar to how you should use\tl_new:Neven though you don't need to, or how you should usevand notofor expanding token list variables. It just makes everything so much tidyer.) Just do\NewDocumentCommand\fpifvalid{}{\my_fp_if_valid:nTF}. – schtandard Aug 08 '19 at 19:26