# In LaTeX3, how do I peek ahead for an active character?

LaTeX3 defines the \peek_catcode_ignore_spaces:NTF which allows me to look ahead at the next token in the stream (ignoring spaces) and test its catcode against those of a slew of standard tokens. So I can do:

\peek_catcode_ignore_spaces:NTF \c_math_toggle_token
{
Uh-oh,~ upcoming~ math!
}
{
Phew!~ Just~ text.
}


to see if what's next is a math shift.

How do I do the same but for an active character? There's no \c_active_token that I can test against. I've tried making one by emulating the code that makes the other tokens, but my attempts have failed dismally. There's a \c_catcode_active_tl that contains an active token, but it contains it with an \exp_not:N so even doing

\exp_last_unbraced:NV \peek_catcode_ignore_spaces:NTF \c_catcode_token_tl


doesn't work.

Yet there is a \token_if_active:NTF so I presume I could cobble together some variant of the peek code that tested for an active character by effectively copying the code that defines \peek_catcode_stuff and swapping the tests. But before I attempt that, I thought I'd ask if there's something I'm missing.

So, is there a straightforward way to see if the next character is active?

You're almost there. \exp_last_unbraced:NV expands \c_catcode_active_tl only once, then

\exp_last_unbraced:NV \peek_catcode_ignore_spaces:NTF \c_catcode_active_tl
{ true } { false }


becomes

\peek_catcode_ignore_spaces:NTF \exp_not:N % <- test token
* % <- true branch
{ true } % <- false branch
{ false } % <- leftover


then things go horribly wrong :)

Since the active token in \c_catcode_active_tl is preceded by \exp_not:N, you can safely x-expand it to expose the actual token:

\ExplSyntaxOn
\cs_new_protected:Npx \test
{
\peek_catcode_ignore_spaces:NTF \c_catcode_active_tl
{ \exp_not:N \__test_aux:Nn \c_true_bool }
{ \exp_not:N \__test_aux:Nn \c_false_bool }
}
\cs_new_protected:Npn \__test_aux:Nn #1 #2
{ \iow_term:x { \bool_if:NF #1 { NOT~an~ } active~token:~\tl_to_str:n {#2} } }
\ExplSyntaxOff

\test ~
\test a
\test \stop
\test \relax
\test {ab}

\stop


The test above prints:

active token: ~
non-active token: a
non-active token: \stop
non-active token: \relax
non-active token: ab


P.S.: There is no \c_catcode_active_token because doing \let \c_catcode_active_token <actual active token> would make \c_catcode_active_token a regular macro with the same definition as the <actual active token>, which would be of no use in this case.

• you beat me to it, but I was going to suggest e not x :-) \exp_args:Ne\peek_catcode_ignore_spaces:NTF \c_catcode_active_tl  Dec 15, 2021 at 12:10
• @DavidCarlisle Since there isn't a \cs_...:Npe then one difference between Phelype's solution and yours is that Phelype's does the expansion once at definition time whereas yours does it every time at invocation. Also, Phelype's expands the whole definition whereas yours only expands the \c_catcode_active_tl. Are there other significant differences? Dec 15, 2021 at 15:37
• Phelype, this is fantastic. It's working perfectly in my code. Dec 15, 2021 at 15:40
• @AndrewStacey no real differences other than that (which is why I didn't post my full example) but I could do if you think it's useful to compare Dec 15, 2021 at 15:40
• @DavidCarlisle I think it's always useful to have alternatives (and one of the things I value having on this site). It could be edited in to this answer as a variation (if Phelype's happy with that), but I don't see any problem with having it as a separate answer either. Dec 15, 2021 at 15:44

An alternative way to extract the active character token from \c_catcode_active_tl is to use \exp_args:Ne:

\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand\zzMath{}{
\peek_catcode_ignore_spaces:NTF \c_math_toggle_token
{
Uh-oh,~ upcoming~ math!
}
{
Phew!~ Just~ text.
}
}

\NewDocumentCommand\zzActive{}{
\exp_args:Ne\peek_catcode_ignore_spaces:NTF \c_catcode_active_tl
{
Uh-oh,~ upcoming~ active!
}
{
Phew!~ Just~ text.
}
}

\ExplSyntaxOff
\begin{document}

[\zzMath abc] [\zzMath $x=2$] [\zzMath ~zz]

[\zzActive abc] [\zzActive $x=2$] [\zzActive ~zz]

\end{document}