The following answer consists of two parts.
\noexpand
's IMPLEMENTATION. In this part I'll outline how a TeX engine might implement the \noexpand
primitive. I'll also establish some terminology that will be used in part 2. Part 1 answers the question posed by OP of how to interpret the following statement
\noexpand
sets the next token to temporarily be \relax.
The implementation I will present is speculative. In other words, I'm not claiming that this is how any of the existing TeX engines are implemented. All I'm saying is that it would be possible to implement a TeX engine along the lines described in this part, and that the resulting implementation will behave in a way that agrees with the existing implementations, as far as \noexpand
is concerned.
STEP-THROUGH EXAMPLES. In this part I'll apply the theory of the first part to four snippets of TeX code, each of which demonstrates how \noexpand
interacts with some other TeX primitive. The four other primitives are, in order of presentation: \edef
, \expandafter
, \ifx
, and e-TeX's \numexpr
.
For every one of these primitives, I will
(a) Suggest how it might be implemented by a TeX engine.
(b) List a TeX code snippet that exhibits an interaction between this primitive and \noexpand
.
(c) Step through the execution of this snippet, given the \noexpand
implementation described in part 1 and the suggested implementation of the other primitive.
1. \noexpand
'S IMPLEMENTATION
Every token t
has a value. The value of t
is that which can be \let
to some control-sequence or active character, as in \let\u=t
. We shall refer to the values initially assigned to TeX's primitive control-sequence \<cs name>
by <cs name>; for instance, the value initially assigned to \meaning
is meaning, and the value initially assigned to \relax
is relax, etc. These will be referred to as the primitives' default values.
The TeX engine maintains a table mapping token names (i.e. what \string t
expands to) to an ordered pair (v,x)
, where v
is the token's value, and x
is a boolean flag to be described below. We shall refer to x
as the token's "temporarily non-expandable" flag. When an entry is added to the table, x
is automatically set to false
, but this can be changed later, as explained below.
This table can be accessed only via an API. This API consists of three functions that have the following signatures:
void temp_nonexpand(string tk_name)
value_obj get_value(string tk_name)
int compare_temp_nonexpand(string tk_name1, string tk_name2)
temp_nonexpand
takes a token name as argument, and sets the "temporarily non-expandable" flag associated with this name to true
. There is no way to explicitly reset this flag to false
; this is done automatically by get_value
and compare_temp_nonexpand
, as explained below.
get_value
takes a token name as argument, and returns (a pointer to) the value mapped to this name. When get_value(...)
is called and passed the name of a control-sequence whose "temporarily non-expandable" flag is true
, get_value
returns the value relax, regardless of what value is actually mapped to this control-sequence, and resets the token's "temporarily non-expandable" flag to false
. Thus, the next time get_value
will be called with the same name, the value actually mapped to this name will be returned. This is the meaning of the quote cited in the beginning of this answer.
compare_temp_nonexpand
takes two token names as arguments, and returns 1 if both tokens' "temporarily non-expandable" flags are true
, -1 if both tokens' "temporarily non-expandable" flags are false
, and 0 otherwise (i.e. one of the flags is true, and the other is false, not necessarily in this order). Like get_value
, compare_temp_nonexpand
resets both tokens' "temporarily non-expandable" flag to false
. This function is used solely by the implementation of ifx; see section 2 below for details.
Every value is either expandable or non-expandable. The user has no control over this property; it is decreed in the TeXbook (20th printing, Addison-Wesley 1991) as follows: the expandable values are the macros, and the default values of some of TeX's primitive control-sequences. A complete list of the expandable primitives can be found on pp. 212-215 of the TeXbook, but here are some examples.
- The expandable values include the default values of the following primitives:
\expandafter
, \ifx
, \meaning
, \noexpand
, \string
, and \the
.
- The non-expandable values include the default values of the following primitives:
\def
, \edef
, \relax
, and \show
.
When a token's value is expandable, we say that the token itself is expandable. To expand the token is to expand its value. What "expanding a value" entails depends on the value. For example, what it means to expand noexpand is this. Denote by u
the next token on the input stream. If u
is expandable, u
's "temporarily non-expandable" flag is set to true
by executing temp_nonexpand(\string u)
.
Some of the non-expandable primitives are executable commands (e.g. all the primitives listed in the last bullet-point), and some are not (e.g. registers, parameters). There is no real difference between "executing" and "expanding", except we apply these verbs to different objects: the former to non-expandable command primitives, whereas the latter to macros and expandable primitives. We shall also apply the word "execute" to internal functions such as get_value
, as we did in the end of the last paragraph.
2. STEP-THROUGH EXAMPLES
Now let's put all this theory to practice, by stepping through four examples. Each example shows how \noexpand
interacts with some other TeX primitive, namely
\edef
\expandafter
\ifx
\numexpr
(an e-TeX primitive)
All the examples assume that the all the primitives featured in them have their default values.
Example 1: \edef
We shall only be concerned with how edef constructs the internal representation of the defined macro's replacement text from the tokens specified inside the curly braces in the input stream. edef implements the following algorithm to construct the replacement text.
- Initialize the token list
replacement_text
to the empty list.
- Remove the first token from the input stream, and assign it to the variable
t
.
- Repeat as long as
t
is not the closing brace:
- Get
t
's value: v := get_value(\string t)
.
- If
v
is non-expandable, append the token to the replacement text: replacement_text.append(
t)
.
- If
v
is expandable, expand v
.
- Remove the first token from the input stream, and assign it to
t
.
Now consider the following TeX code (taken from Hendrik Vogt's answer):
\edef\bar{\noexpand\foo\anothercs}
Let's step through the construction of the replacement text.
- The
replacement_text
token-list is initialized to the empty list.
\noexpand
is removed from the input stream and assigned to t
.
\noexpand
's value is assigned to v
: v := get_value("noexpand")
, so now v ==
nonexpand.
- Since noexpand is expandable, it is expanded, by executing
temp_nonexpand("foo")
. This turns on \foo
's "temporarily non-expandable" flag.
\foo
is removed from the input stream, and assigned to t
.
- We set
v := get_value("foo")
. Since \foo
's "temporarily non-expandable" flag is true
when get_value
is invoked, v==
relax, and the flag is turned off.
- Since relax is non-expandable,
\foo
is appended to the replacement_text
list.
\anothercs
is removed from the input stream, and assigned to t
.
- If
\anothercs
's value is non-expandable, \anothercs
will be appended to the replacement_text
list; otherwise, it will be expanded, and so on.
Example 2: \expandafter
expandafter is implemented as follows. It is assumed that there is a dedicated global token stack s
intended for expandafter's use. s
can be assumed to be initially empty, though this fact will not be used here.
- Remove the first token from the input stream, and push it on the stack
s
.
- Remove the first token from the input stream, and assign it to the variable
t
.
- Retrieve
t
's value: v := get_value(t)
.
- If
v
is non-expandable, push t
back to the front of the input stream. Otherwise, expand it.
- Pop the token at the top of
s
, and add it to the front of the input stream.
Now consider the following TeX manuscript (taken from Hendrik Vogt's answer):
\def\foo{bar}%
\expandafter\expandafter\expandafter\show\noexpand\foo%
\bye
Let's step through the expansion of the second line.
\expandafter
is removed from the input stream, and its value, expandafter, is retrieved. Since expandafter is expandable, it is expanded, as follows.
- The 2nd occurrence of
\expandafter
is removed from the input stream, and is pushed on s
, so the stack now holds only this one token.
- The 3rd occurrence of
\expandafter
is removed from the input stream, and assigned to t
.
- We set
v := get_value("expandafter")
. Now v ==
expandafter.
Since expandafter is expandable, it is expanded, as follows.
1) \show
is removed from the input stream, and pushed on s
, so that s
now holds \show\expandafter
, where the top of the stack is on the left.
2) \noexpand
is removed from the input stream, and assigned to t
.
3) We set v := get_value("noexpand")
. Now v ==
noexpand.
4) Since nonexpand is expandable, it is expanded, by executing temp_nonexpand("foo")
. This turns on \foo
's "temporarily non-expandable" flag.
5) s
is popped, and the popped item, namely \show
, is added to the front of the input stream, which now looks as follows:
\show\foo
s
is popped, and the popped item, namely \expandafter
, is added to the front of the input stream, which now looks as follows:
\expandafter\show\foo
\expandafter
is removed from the input stream, and its value, expandafter, is retrieved. Since expandafter is expandable, it is expanded, as follows.
\show
is removed from the input stream, and is pushed on s
.
\foo
is removed from the input stream, and assigned to t
.
- We set
v := get_value("foo")
. Since at the time get_value
is called, \foo
's "temporarily non-expandable" flag has been true
since step 1.4.4, v ==
relax, and \foo
's "temporarily non-expandable" flag is turned off.
- Since relax is non-expandable,
\foo
is pushed back to the front of the input stream.
s
is popped, and the popped item, namely \show
, is added to the beginning of the input stream. The input stream is now:
\show\foo
\show
is removed from the input stream, and its value, show, is retrieved. Since show is a command, it is executed: \foo
's value is retrieved, as follows. First we set v := get_value("foo")
, and then a representation of v
is written to the log file. Since the get_value
call in step 2.3 has turned off \foo
's "temporarily non-expandable" flag, v == (macro:->bar)
.
Example 3: \ifx
ifx is implemented as follows.
- Remove the first two tokens from the input stream, and assign them to the variables
t
and u
, respectively.
- Set
c := compare_temp_nonexpand(\string t, \string u)
. As a side-effect, this operation sets the "temporarily non-expandable" flags of both tokens to false
.
- If
c == ...
1
, i.e. if both tokens were temporarily non-expandable, the test evaluates to true
.
0
, i.e. if exactly one of the tokens was temporarily non-expandable, the test evaluates to false
.
-1
, i.e. if neither token was temporarily non-expandable, the test evaluates according to the usual rules (see the TeXbook, p. 210).
- Once the test's value is known, ifx's expansion proceeds as usual (see the TeXbook, p. 213).
For example, the following TeX manuscript:
\def\foo{foo}%
\def\bar{bar}%
\expandafter\expandafter%
\expandafter\ifx%
\expandafter\noexpand\expandafter\foo\noexpand\bar yes%
\else no%
\fi%
\bye
typesets "yes", even though \foo
and \bar
don't have the same \meaning
, because at the time the test is performed, both tokens are temporarily non-expandable.
However, the following manuscript:
\def\foo{foo}%
\expandafter\ifx\noexpand\foo\relax yes\else no\fi%
\bye
typesets "no", even though \noexpand\foo
and \relax
have the same meaning (try \expandafter\meaning\noexpand\foo
), because at the time the test is performed, \foo
's "temporarily non-expandable" flag is true
, whereas \relax
's "temporarily non-expandable" flag is false
.
The following caveat should be noted. ifx is the only TeX primitive whose implementation invokes compare_temp_nonexpand
. In particular, neither \if
, nor any of the other conditionals (e.g. \ifcat
) do so. Therefore, if you substitute \if
for \ifx
above, both tests will evaluate to true
, since the temporarily non-expandable tokens will be interpreted as \relax
.
Example 4: \numexpr
numexpr is a non-expandable primitive command that is not a part of core TeX, but of the e-TeX extension, which is implemented by pdftex
. I shall describe a simplified version of this algorithm, which behaves somewhat differently than the original, but is sufficiently close to it to exemplify how numexpr and noexpand interact for real.
The version of numexpr that will be described below takes as input a sequence of non-negative integers interspersed with binary arithmetic operators, e.g. 1+20*3
, and assigns the result of the expression to a new integer register \return
. No whitespace is allowed inside the expression. Any token that is neither a digit nor a binary arithmetic operator marks the end of numexpr's input. The operations are executed in the textual order, so, for instance, the expression above will evaluate to 63 (= (1 + 20) * 3) rather than to 61. numexpr removes its input from the input stream. If the expression is followed by a token whose value is relax, this token is also removed from the input stream (which is how the real numexpr primitive behaves; see p. 8 of the e-Tex manual).
The numexpr algorithm makes use of two auxiliary functions: get_next_nonexpandable
and parse_nonneg_int
.
get_next_nonexpandable
get_next_nonexpandable
gets the next non-expandable token from the input stream by repeatedly expanding the first item, if necessary. The token's value is also returned (this point is critical if the example below is to behave the way it does in real life). Its signature is
(token, value) get_next_nonexpandable()
and its implementation is:
- Remove the first token from the input stream, and assign it to the variable
t
.
- Get
t
's value: v := get_value(\string t)
.
- Repeat as long as
v
is expandable:
- Expand
v
.
- Remove the first token from the input stream, and assign it to the variable
t
.
v := get_value(\string t)
- Return
(t, v)
.
parse_nonneg_int
parse_nonneg_int
removes the longest possible sequence of digits from the beginning of the input stream, and returns the non-negative integer represented by this string of digits. Its signature is
int parse_nonneg_int()
and its implementation is:
- Initialize the non-negative integer variable
D
to 0
. This variable will accumulate the intermediate calculations, and will eventually hold the number to be returned.
- Get the next non-expandable token from the beginning of the input stream:
(t, v):= get_next_nonexpandable()
.
- Repeat as long as
v
is of the form (character, catcode)
and the character is a digit.
- Denote
v
's character by c
.
- Convert
c
to an integer d
.
- Set
D := D*10 + d
.
- Get the next non-expandable token from the beginning of the input stream:
(t, v):= get_next_nonexpandable()
.
- Push
t
back to the beginning of the input stream.
- Return
D
.
numexpr
Using get_next_nonexpandable
and parse_nonneg_int
, the simplified version of numexpr's algorithm is:
- Initialize the integer variable
i := 0
. This variable will accumulate the intermediate calculations, and will eventually hold the result of the entire arithmetic expression.
- Initialize the character variable
op:='+'
. This variable will hold the most recently encountered arithmetic operator.
- Get the next non-expandable token from the input stream:
(t,v) := get_next_nonexpandable()
.
- Repeat as long as
v
is of the form (character, catcode)
, and the character is either a digit or a binary arithmetic operator.
- Denote
v
's character by c
.
- If
c
is a digit:
1) Push t
back to the beginning of the input stream.
2) Parse the longest possible non-negative integer from the beginning of the input stream: j := parse_nonneg_int()
.
3) Perform the operation designated by op
on the arguments i
and j
.
4) Set op
to null
.
- If
c
is a binary arithmetic operator:
1) If op != null
, report an error ("2 consecutive operators!"), and exit the TeX engine.
2) If op == null
, assign op := c
.
- Get the next non-expandable token from the input stream:
(t,v) := get_next_nonexpandable()
.
- If
op != null
, report an error ("Dangling operator!"), and exit the TeX engine.
- If
v !=
relax, push t
back to the beginning of the input stream.
- Allocate a new integer register:
\newcount\result
.
- Assign
i
to the integer register: \result{<tokenized representation of i>}
.
- Add the token
\result
to the beginning of the input stream.
Now consider the following TeX code (which is a slightly simplified version of the code proposed by Martin Scharrer in a comment to Hendrik's answer):
\the\numexpr 1+1\noexpand\empty\relax
Let's step through the processing of this line, starting with \numexpr
. In other words, the TeX engine is in the middle of expanding the, and has just begun executing numexpr, so that the input stream is currently 1+1\noexpand\empty\relax
. We assume that \empty
is a macro that is defined as follows: \def\empty{}
.
- Initialize the integer variable
i:=0
, and a character variable op:='+'
.
(t, v) := get_next_nonexpandable()
. t
is now the token 1
with value ('1', 12)
(12 is the default catcode for digits).
- since
1
is a digit, t
is pushed back to the front of the input stream, and we set j := parse_nonneg_int()
. j
is now equal to the integer 1
, and the input stream is +1\noexpand\empty\relax
.
- Assign
i := i + j
. i
is now equal to 1
.
(t, v) := get_next_nonexpandable()
. t
is now the token +
with value ('+',12)
(12 is the default catcode for the character '+').
- Since
+
is a binary arithmetic operator, we assign op:='+'
.
(t, v) := get_next_nonexpandable()
. t
is now the token 1
with value ('1', 12)
.
since 1
is a digit, t
is pushed back to the front of the input stream, and we set j := parse_nonneg_int()
. Let's step through the execution of parse_nonneg_int
.
- The nonnegative integer variable
D
is initialized to 0
.
(t, v) := get_next_nonexpandable()
. t
is now the token 1
, and v
is ('1', 12)
.
- Since
1
is a digit, it is converted to the corresponding integer d:=1
, and we set D:=D*10+d
. D
is now equal to 1
.
(t, v) := get_next_nonexpandable()
. Let's step through the execution of get_next_nonexpandable
.
1) The first token is removed from the input stream and assigned to the variable t
. So now t == \noexpand
.
2) t
's value is retrieved and assigned to the variable v
: v:=get_value("noexpand")
. So now v ==
noexpand.
3) Since noexpand is expandable, it is expanded, by executing set_temp_nonexpand("empty")
. So now \empty
is flagged as temporarily non-expandable.
4) The first token of the input stream is removed from the input stream and assigned to t
. So now t == \empty
.
5) t
's value is retrieved and assigned to v
: v:=get_value("empty")
. Since at the time get_value
is called, \empty
is flagged as temporarily non-expandable, get_value
returns relax, and turns off \empty
's "temporarily non-expandable" flag. So now v ==
relax, and \empty
is no longer temporarily non-expandable.
6) Since relax is non-expandable, (t, v)
, i.e. (\empty,
relax)
, is returned.
Since v==
relax is not of the form (character, catcode)
, t
is pushed back to the beginning of the input stream, and parse_nonneg_int
returns D
, i.e. the integer 1
. The input stream is now
\empty\relax
Now i == 1
, j == 1
, and op == '+'
, and we set i := i + j
, so that now i == 2
, and we set op := null
.
(t, v) := get_next_nonexpandable()
. Let's step through the execution of get_next_nonexpandable
.
- The first token of the input stream is removed from the input stream, and assigned to
t
, so that t == \empty
.
t
's value is retrieved and assigned to v
: v := get_value("empty")
. Recall that \empty
's "temporarily non-expandable" flag has been false
since the end of step 8.4.5. So get_value
returns \empty
's actual value, namely the empty macro.
- Since
v
is a macro, it is expandable, so \empty
is expanded. However, since \empty
's body is empty, the input stream remains intact (and, of course, so does the TeX engine's internal state). So after \empty
's expansion the input stream consists of a single token: \relax
.
- The first token is removed from the input stream, and assigned to
t
, so that t == \relax
.
t
's value is retrieved and assigned to v
: v := get_value("relax")
. So now v ==
relax.
- Since relax is non-expandable,
get_next_nonexpandable
returns (t, v)
, i.e. (\relax,
relax)
.
- Since
v ==
relax, numexpr has reached the end of its loop (step 4 in numexpr's algorithm). So we allocate a new integer register \result
: \newcount\result
.
- We assign
i
to \result
: \result{2}
.
We add the token \result
to the beginning of the input stream, so that now the input stream consists of a single token: \result
, and this input stream is "handed over" to the the primitive to act on. So the end result will be as though the TeX engine was originally presented with the input stream
\the\result
with \result
being an integer register holding the value 2.
- The text "2" is typeset.
\message{\ifnum0=0\noexpand\foo\fi}
. the\noexpand
"coating" is removed by\ifnum
, and we then have\message{\foo}
, which tries to expand\foo
. --- Other relevant thing: try\noexpand\section
in your document.\message{\foo}
". What happens is the following:\message
expands it's contents, the expansion of\ifnum0=0
triggers expansion of\noexpand
before the\ifnum
comparison is evaluated since there's no space after0=0
, and then TeX continues to expand\foo
after seeing that0=0
. But maybe this is what you meant.\noexpand
coating is removed from\foo
by\ifnum
, and that\foo
is then bare when fighting against the corrosive\message
. If we add a space, as you mention,\message
sees\noexpand\foo
, and prints\foo
. So basically, yes, we're saying the same thing.