How to \futurelet the token after a space

Inspired by wipet's answer I suggest a \let-\afterassignment-loop:

\def\process#1{\afterassignment\doA\let\next= #1\process\process}
\def\doA{%
  \ifx\next\process\else
    \message{\meaning\next}%
    \afterassignment\doA
  \fi
  \let\next= %
}
    
\process{ ab}

\message{done}

\end

(It is assumed that the argument of \process does not contain tokens whose meaning equals the meaning of \process.)

Console output is:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdftex)
 restricted \write18 enabled.
entering extended mode
(./test.tex blank space   the letter a the letter b done )
No pages of output.
Transcript written on test.log.

To your "related question":

Instead of

\def\defmyspace#1{\futurelet\myspace\relax#1}
\defmyspace{ }
\show\myspace 

take into account that \let ... = ... consumes exactly one optional space behind = and do something like:

\def\myspace#1{#1}
\myspace{\let\myspace= } %
\show\myspace

As long as the \lccode of the space-character and the \lccode of the =-character do not indicate that \lowercase affects these characters you can also do:

\lowercase{\let\myspace= } %
\show\myspace

In LaTeX 2ε \@sptoken is available.



As Wipet already provided an excellent answer exhibiting the usage of \let, \futurelet and \afterassignment I decided to provide a totally different approach to the matter—one which probably is suitable if you actually wish to "look" at things argumentwise.

Depending on what you intend to do exactly, you can probably tail-recursively iterate without assignments/without \futurelet/\let:

%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\chardef\stopromannumeral=`\^^00%
\secondoftwo{}{\long\def\removespace} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\long\def\CheckWhetherNull#1{%
  \romannumeral\expandafter\secondoftwo\string{\expandafter
  \secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\stopromannumeral\secondoftwo}%
  {\expandafter\stopromannumeral\firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit 
%% catcode-1-character-token
%%.............................................................................
%% \CheckWhetherBrace{<Argument which is to be checked>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked has leading
%%                     explicit catcode-1-character-token>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked does not have a leading
%%                     explicit catcode-1-character-token>}%
\long\def\CheckWhetherBrace#1{%
  \romannumeral\expandafter\secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\stopromannumeral\firstoftwo}%
  {\expandafter\stopromannumeral\secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                  {<Tokens to be delivered in case
%%                                     <argument which is to be checked>
%%                                    does have a leading explicit space-
%%                                    token>}%
%%                                  {<Tokens to be delivered in case
%%                                     <argument which is to be checked>
%%                                    does not have a leading explicit 
%%                                    space-token>}%
\long\def\CheckWhetherLeadingExplicitSpace#1{%
  \romannumeral\CheckWhetherNull{#1}%
  {\expandafter\stopromannumeral\secondoftwo}%
  {%
    % Let's nest things into \firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\stopromannumeral
      \romannumeral\expandafter\secondoftwo
      \string{\CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\long\def\CheckWhetherLeadingExplicitSpaceB#1 {%
  \expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}%
  {\exchange{\firstoftwo}}{\exchange{\secondoftwo}}%
  {\expandafter\expandafter\expandafter\stopromannumeral
   \expandafter\expandafter\expandafter}%
  \expandafter\secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \romannumeral\ExtractFirstArgLoop{ABCDE\SelDOm} yields  A
%%
%%   \romannumeral\ExtractFirstArgLoop{{AB}CDE\SelDOm} yields  AB
%%
%%   \romannumeral\ExtractFirstArgLoop{\SelDOm ABCDE\SelDOm} yields  \SelDOm
%%.............................................................................
\long\def\RemoveTillSelDOm#1#2\SelDOm{{#1}}%
\long\def\ExtractFirstArgLoop#1{%
  \expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}%
  {\expandafter\stopromannumeral\secondoftwo{}#1}%
  {\expandafter\ExtractFirstArgLoop\expandafter{\RemoveTillSelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%%
\long\def\process#1{%
  \CheckWhetherNull{#1}{}{%
    \CheckWhetherLeadingExplicitSpace{#1}{%
      The first token of {\tt\string\process} is an explicit space token.\hfill\break
      \expandafter\process\expandafter{\removespace#1}%
    }{%
       \CheckWhetherBrace{#1}{%
          The first token of {\tt\string\process} is an opening brace.\hfill\break
          \expandafter\process\expandafter{\romannumeral\ExtractFirstArgLoop{#1\SelDOm}}%
          The first token of {\tt\string\process} is a closing brace.\hfill\break
       }{%
          The first token of {\tt\string\process} is: {\tt\expandafter\string\romannumeral\ExtractFirstArgLoop{#1\SelDOm}}\hfill\break
       }%
       \expandafter\process\expandafter{\firstoftwo{}#1}%
    }%
  }%
}%

\noindent{\tt\string\noindent}\hfill\break
{\tt\string\process\string{ ab\string{cd\string}e f \string\TeX\string}}

\bigskip

\noindent
\process{ ab{cd}e f \TeX}%

\bye

enter image description here

From a comment of yours I learned that the issue is about stringification of an argument in LaTeX 2ε.

Be aware that the approach above is not suitable, e.g., for proper stringification of arguments because matching curly braces are consumed without actually examining the closing brace.

I can offer a stringification-routine which examines opening- and closing braces. (Instead of {1/}2 there could be something like X1/Y2 or even worse 1/2 ...)

%% Copyright (C) 2019, 2020 by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
    
\errorcontextlines=10000
    
\documentclass{article}

\makeatletter    
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked> does have a
%%                               leading explicit space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked> does not have a
%%                               a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
  \long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the 
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo}  whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
  \romannumeral
  \expandafter\UD@ExtractFirstArgLoop\expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\expandafter
    \expandafter            {%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\string
    \expandafter}%
    \string#1%
  \UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, remove everything till 
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
   \romannumeral\expandafter\expandafter\expandafter
                \UD@StringifyClosingBraceloop
                \UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
  \expandafter\UD@CheckWhetherLeadingExplicitSpace\expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\UD@secondoftwo
    \expandafter{%
    \expandafter}%
    \expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\string
    \expandafter}%
    \string#1%
  }%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
  \expandafter\expandafter\expandafter\UD@stopromannumeral%
  \expandafter\expandafter
  \expandafter{%
  \expandafter\string      
  \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
  \expandafter#1%
  \string#2%
  }%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
  \expandafter\UD@PassFirstToSecond\expandafter{\expandafter
  {\romannumeral\expandafter\UD@secondoftwo\string}{}%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
      \UD@Exchange{\UD@removespace}%
    }{%
      \UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
    }{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \expandafter#1%
      \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
      \expandafter
    }%
    \string#4%
  }{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
      \UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
    }{%
      \UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
    }%
    {#2}%
  }{%
    \UD@CheckWhetherLeadingExplicitSpace{#1}{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@removespace}{\UD@removespace}%
    }{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
    }%
    {#1}{#2}%
  }%
}%
%%-----------------------------------------------------------------------------    
%% Apply <action> to the stringification of each token of the argument:
%%
%% \StringifyNAct{<action>}{<token 1><token 2>...<token n>}
%%
%% yields:  <action>{<stringification of token 1>}%
%%          <action>{<stringification of token 2>}%
%%          ...
%%          <action>{<stringification of token n>}%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%% Due to \romannumeral-expansion the result is delivered after two
%% \expandafter-chains.
%% If you leave <action> empty, you can apply a loop on the list formed by
%%   {<stringification of token 1>}%
%%   {<stringification of token 2>}%
%%   ...
%%   {<stringification of token n>}%
%%.............................................................................
\newcommand\StringifyNAct{%
  \romannumeral\StringifyNActLoop{}%
}%
%%.............................................................................
%% \StringifyNActLoop{{<stringification of token 1>}...{<stringification of token k-1>}}%
%%                   {<action>}%
%%                   {<token k>...<token n>}
%%.............................................................................
\newcommand\StringifyNActLoop[3]{%
  \UD@CheckWhetherNull{#3}{%
    \UD@stopromannumeral#1%
  }{%
    \UD@CheckWhetherBrace{#3}{%
      \expandafter\expandafter\expandafter\UD@Exchange
      \expandafter\expandafter\expandafter{%
        \UD@StringifyClosingBrace{#3}%
      }{%
        \expandafter\StringifyNActLoop\expandafter{%
          \romannumeral
          \expandafter\expandafter\expandafter\UD@Exchange
          \expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#3}}{\StringifyNActLoop{#1}{#2}}%
        }{#2}%
      }%
    }{%
      \UD@CheckWhetherLeadingExplicitSpace{#3}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#3}{%
          \StringifyNActLoop{#1#2{ }}{#2}%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
          \expandafter\StringifyNActLoop\expandafter{%
             \romannumeral%
             \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@PassFirstToSecond
             \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
               \expandafter\expandafter\expandafter\string
               \expandafter\UD@Exchange
               \romannumeral\UD@ExtractFirstArgLoop{#3\UD@SelDOm}{}%
             }{\UD@stopromannumeral#1#2}%
          }%
          {#2}%
        }%
      }%
    }%
  }%
}%
%%.............................................................................
%% Now a routine which you can apply as <action> within \StringifyNAct:
%%.............................................................................
\newcommand\printstringifiedtoken[1]{%
  A token was stringified as
  \UD@CheckWhetherLeadingExplicitSpace{#1}{%
    \fbox{\texttt{\char`\ \strut}} (explicit space token\strut)%
  }{%
    \fbox{\texttt{#1\strut}}%
  } 
 .\\
}%
%%.............................................................................
%% Now a routine which you can apply when prefering iterating on the result
%% of \StringifyNAct
%%.............................................................................
\newcommand\printstringifiedtokenloop[1]{%
  \ifx\relax#1\expandafter\@gobble\else\expandafter\@firstofone\fi
  {\printstringifiedtoken{#1}\printstringifiedtokenloop}%
}%
\makeatother

\pagestyle{empty}

\begin{document}

\vspace*{-4cm}\enlargethispage{4cm}

\begin{verbatim*}
\noindent
\StringifyNAct{\printstringifiedtoken}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}
\end{verbatim*}

yields:\bigskip

\noindent
\StringifyNAct{\printstringifiedtoken}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}

(The last explicit space token is due to the \verb|\endlinechar|-thingie 
while the state of \LaTeX's reading-apparatus is in state M (middle of line) 
after a curly closing brace. It also is in that state after an opening
curly brace.)

\newpage
\vspace*{-4cm}\enlargethispage{4cm}

\begin{verbatim*}
\noindent
\expandafter\expandafter
\expandafter\printstringifiedtokenloop
\StringifyNAct{}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}%
\relax
\end{verbatim*}

yields:\bigskip

\noindent
\expandafter\expandafter
\expandafter\printstringifiedtokenloop
\StringifyNAct{}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}%
\relax

(The last explicit space token is due to the \verb|\endlinechar|-thingie 
while the state of \LaTeX's reading-apparatus is in state M (middle of line) 
after a curly closing brace. It also is in that state after an opening
curly brace.)
\end{document}

enter image description here

enter image description here


You can \process{ ab} with \futurelet by following macros:

\def\process#1{\doA#1\end}
\def\doA{\futurelet\fl\doB}
\def\doB{\ifx\fl\end\else \reportfl \afterassignment\doA \fi \let\next= } 
\def\reportfl{\message{\meaning\fl}}

\process{ ab}

\end

I corrected a bug in my code (Ulrich's and Donald's comments) and moved the reporting about \fl to a macro in order to tokens like \if, \fi can be processed too.


The tokcycle package is set up for cycling through tokens. This is the simplest demonstration, showing that you define what to do with each token...here, I bracket characters, and paren the spaces.

A more sophisticated directive can test the tokens and branch according to their charcode or catcode.

Spaces have their own directive because the way TeX absorbs them them is different, as you noted in your question.

I should note that the normal way one uses tokcycle, the processed tokens are added to a toks register, and you can regurgitate it at the end. Here, because of the simplicity of the demonstration (the tokens did not interact with each other, as arguments, for example), I forewent the \cytoks and just processed the tokens on the fly.

Finally, I will note that the MWE uses the macro form \tokcyclexpress{...} that takes an argument. The package also provides a pseudo-environment form \tokencyclexpress...\endtokencyclexpress.

\documentclass {article}
\usepackage{tokcycle}
\begin{document}
\Characterdirective{[#1]}
\Spacedirective{(#1)}
\tokcyclexpress{ ab}
\end{document}

enter image description here

Here is an example of a slightly more sophisticated character directive, in which a tokens are, additionally, made italic:

\Characterdirective{[\tctestifx{a#1}{\textit{#1}}{#1}]}

enter image description here


SUPPLEMENT

And now, for fun, I demonstrate how tokcycle can track the active and implicit nature of tokens, as well. Note in example 3, the a is not italicized, because it is handled by the \Macrodirective, being defined via \def, rather than \let. The macro directive is set up to provide the token between braces.

\documentclass {article}
\usepackage{tokcycle}
\begin{document}
\newcommand\checkAI{$\ifimplicittok_\mathrm{implicit}\fi
   \ifactivetok^\mathrm{active}\fi$}
\Characterdirective{[\tctestifx{a#1}{\textit{#1}}{#1}]\checkAI}
\Macrodirective{\{#1\}\checkAI}
\Spacedirective{(#1)\checkAI}

1. \tokcyclexpress{ ab}

2.\let\q a
\tokcyclexpress{ \q b}

3.\catcode`Q=\active
\defQ{a}
\tokcyclexpress{ Qb}

4.\letQa
\tokcyclexpress{ Qb}

5. \def\:{\let\z= }\:
\tokcyclexpress{\z Qb}

6. \catcode`Z=\active
\def\:{\let Z= }\:
\tokcyclexpress{ZQb}

7. \tokcyclexpress{\space Qb}
\end{document}

enter image description here