10

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.

  • 1
    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). – Manuel Mar 28 '15 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 – Joseph Wright Mar 28 '15 at 21:48
5

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? – Manuel Mar 30 '15 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. – egreg Mar 30 '15 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. – mhchem Mar 30 '15 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 – egreg Mar 30 '15 at 12:26
  • @egreg, I was wrong. Mixed up T and F clause. Fixed it. Is does not work. But why? – mhchem Mar 30 '15 at 12:30
4

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 { .. } { .. } { .. }.

2

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.

enter image description here

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.