5

I've found the following code in another question:

\def\scan#1{\scanA#1*}
\def\scanA{\futurelet\next\scanB}
\def\scanB{\expandafter\ifx\next\space \expandafter\scanC \else \expandafter\scanE \fi}
\def\scanC{\afterassignment\scanD \let\next= }
\def\scanD{\scanE{ }}
\def\scanE#1{\ifx*#1\else \dosomething{#1} \expandafter\scanA \fi}

The effect is to convert a string like ABC into \dosomething{A}\dosomething{B}\dosomething{C}.

If \let\dosomething=\uppercase, then \scan{Hello World!} obtains HELLO WORLD!.

I understand when \ifx\next\space in \scanB gets false, it expands to \scanE, which captures the next unread token as its parameter and \dosomething.

However, when it gets true, it expands to \scanC. \scanD expands to \scanE{ } so that we may handle the spaces. But why the next \scanA will skip the space, and how \afterassignment and \let\next= work?

===== update by OrthoPole =====

I think I've understood the process myself.

When the \ifx get true, the stream looks like \futurelet·\next·\scanB·_·... (_ stands for a space token), and it expands to \afterassignment·\scanD·\let·\next·=·_·_·.... We see \let allows an optional space token, hence the first space after = will be omitted, and \next will be assigned the second space. After this assignment, \scanD will be expanded. Since the two spaces after = are absorbed by \let, we get \scanD·....

OrthoPole
  • 113
  • 1
    By the way there's also the package unravel which allows you to walk through single steps by emulating parts of TeX. For TeX primitives I think it can be quite helpful. https://tex.stackexchange.com/questions/61010/a-latex-log-analyzer-application-visualizing-tex-expansion – user202729 Apr 12 '23 at 08:20

1 Answers1

5

\afterassignment\cs will reinsert \cs in the input stream as soon as the next assignment has been performed.

What's considered as an assignment? Setting the value of a register (but for box registers it's slightly more complicated); doing \.def where . stands for nothing, g, x, char and so on; doing \let.

Your case is the last one. Now the syntax for \let is

\let <cs><optional spaces><optional =><one optional space>

The <optional spaces> part is not interesting in your case, because you always have \let\scanD. More interesting is <one optional space>. In this case TeX does no expansion of the token following =, as witnessed by

\let\x=\space\message{abc}

that prints abc on the console and makes \x to be the same as \space.

Doing

\def\scanC{\afterassignment\scanD \let\next}

would fail if the token after \scanC is =, because it would be taken as part of the \let instruction, which isn't wanted. But also

\def\scanC{\afterassignment\scanD \let\next=}

would fail if the token after \scanC is an explicit space token, because it would be taken as <one optional space>.

You want instead to assign to \next the next token, so you need

\def\scanC{\afterassignment\scanD \let\next= }
egreg
  • 1,121,712