# Peek ahead for a digit (expl3) – or any regex, for that matter

Is there an easy way to peek ahead for a digit? Something like

\peek_charcode:NTF [0-9]


The approach to nest 10 \peek_charcode:NTF is quite cumbersome.

I want to match digits only. Digits, parenthesis and others share the same catcode.

• I don't really know if there are options, but I think that some sort of \peek_case:nTF { {1}{..} {2}{..} } { .. } { .. } would be nice (although the name is not correct since all case functions are expandable, something of that kind would be nice). Mar 28, 2015 at 21:47
• @Manuel We've had a request for such a thing but it really doesn't fit with TeX's way of working that well Mar 28, 2015 at 21:48

Standing on the shoulders of giants and extending the idea of Manuel, I came up with this solution. I think, a \regex_peek:nTF would be so versatile that it should be added to l3regex.

\documentclass{scrartcl}
\usepackage{expl3}
\usepackage{l3regex}
\begin{document}
\ExplSyntaxOn
\cs_generate_variant:Nn \regex_match:nnTF { no }

\cs_new_protected:Npn \mh_regex_peek:nTF #1 #2 #3
{
\peek_catcode:NTF ##
{#3}
{
\regex_match:noTF
{ \A the\  (character|letter)\ (#1) \Z }
{ \token_to_meaning:N \l_peek_token } {#2} {#3}
}
}

\mh_regex_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } 1 \par
\mh_regex_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } 2 \par
\mh_regex_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } ( \par
\mh_regex_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } {1} ~ (group) \par
\mh_regex_peek:nTF { [a] } { is ~ letter ~ a: ~ } { not ~ letter ~ a: ~ } 1 \par
\mh_regex_peek:nTF { [a] } { is ~ letter ~ a: ~ } { not ~ letter ~ a: ~ } a \par
\mh_regex_peek:nTF { [abc] } { is ~  a,  ~ b ~  or ~ c: ~ } { not ~  a,  ~ b ~  or ~ c: ~ } b \par
\mh_regex_peek:nTF { a|b|c } { is ~  a,  ~ b ~  or ~ c: ~ } { not ~ a,  ~ b ~  or ~ c: ~ } b \par
\mh_regex_peek:nTF { a|b|c } { is ~  a,  ~ b ~  or ~ c: ~ } { not ~ a,  ~ b ~  or ~ c: ~ } e \par
\end{document}


Edit: Made more robust, by filtering out #

Edit: Switched to the regex { \A the\ (character|letter)\ (#1) \Z }. If anybody knows a nice way to convert \l_peek_token into a token list, then I could get rid of this workaround. PS. It seems that egreg had a very similar idea at the same time.

PPS This solution is being used in mhchem v4.00, now.

• The \cs_set_nopar:Npn should be also protected, because it's not expandable. In any case, what's the intention of expanding \token_to_meaning:N \l_peek_token? Is it correct? Mar 30, 2015 at 11:08
• If \l_peek_token has been set to 0, then \token_to_meaning:N \l_peek_token will expand to the character 0, so the search string a will match. Mar 30, 2015 at 11:50
• @egreg: Can you explain? I added another example  \mh_regex_peek:nTF { [a] } { not ~ letter ~ a: ~ } { is ~ letter ~ a: ~ } 0 \par that works for me. Mar 30, 2015 at 12:16
• Did you try the last one with a instead of 0? Yes, I was wrong in my comment above, but only because of the category codes assigned by \token_to_meaning:N Mar 30, 2015 at 12:26
• @egreg, I was wrong. Mixed up T and F clause. Fixed it. Is does not work. But why? Mar 30, 2015 at 12:30

A starting point (coming from someone who knows nothing about the deep internals of expl3, this, to me, just seems to work). I would use \token_case:NnTF but that function doesn't exist.

\documentclass{scrartcl}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Npn \mh_peek_case:nTF #1 #2 #3
{
\cs_set_nopar:Npn \__mh_peek_case:w
{
\tl_case:NnTF \l_peek_token { #1 } { #2 } { #3 }
}
\peek_after:Nw \__mh_peek_case:w
}

\NewDocumentCommand \checkfordigit { }
{
\mh_peek_case:nTF
{
{0}{} {1}{} {2}{} {3}{} {4}{}
{5}{} {6}{} {7}{} {8}{} {9}{}
}
{ Next ~ token ~ is ~ a ~ digit: ~ }
{ Next ~ token ~ is ~ not ~ a ~ digit: ~ }
}
\ExplSyntaxOff

\begin{document}
\checkfordigit 1 \par
\checkfordigit 2 \par
\checkfordigit A \par
\checkfordigit -- \par
\end{document}


In case you don't care if it's \checkfordigit{1} you could define it just with \tl_set:Nn \l_tmpa_tl {#1} \tl_case:NnTF \l_tmpa_tl { .. } { .. } { .. }.

The idea of \peek_regex:NTF is very nice, but it has a problem: the expansion of \token_to_meaning:N \l_peek_token, when the following token is 0 is

the character 0


so in your case

\mh_peek_regex:nTF { a } { true } { false } 0


will return true, because a matches the character 0.

If you limit yourself to looking for characters and not other tokens, then the idea can be made to work, still allowing ranges in the first argument:

\documentclass{scrartcl}
\usepackage{expl3}
\usepackage{l3regex}
\begin{document}
\ExplSyntaxOn
\cs_generate_variant:Nn \regex_match:nnTF { nV }
\tl_new:N \l__mh_char_peek_tl

\cs_new_protected:Npn \mh_char_peek:nTF #1 #2 #3
{
\peek_catcode:NTF ##
{ #3 }
{
\tl_set:Nx \l__mh_char_peek_tl { \token_to_meaning:N \l_peek_token }
\regex_match:nVTF { \A the } \l__mh_char_peek_tl
{
\regex_replace_once:nnN { \A the \s .* \s } { } \l__mh_char_peek_tl
\regex_match:nVTF {#1} \l__mh_char_peek_tl {#2} {#3}
}
{ #3 }
}
}

\mh_char_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } 1 \par
\mh_char_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } 2 \par
\mh_char_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } ( \par
\mh_char_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } {1} ~ (group) \par
\mh_char_peek:nTF { [0-9] } { digit: ~ } { no ~ digit: ~ } - \par
\mh_char_peek:nTF { a } { is ~ letter ~ a: ~ } { not ~ letter ~ a: ~ } 0 \par
\mh_char_peek:nTF { [a-z] } { is ~ letter ~ a: ~ } { not ~ letter ~ a: ~ } a \par

\end{document}


I check that the expansion of \token_to_meaning:N \l_peek_token starts with the and, in this case I know it will either be the character <?> or the letter <?> so I can remove the unwanted tokens from that expansion and do the correct check.

Tests for other tokens can go on with the appropriate \peek_...:NTF function.