Is it possible to test whether a token register is empty without expanding it?
-
See also conditionals - Expandable test for an empty token list—methods, performance, and robustness - TeX - LaTeX Stack Exchange – user202729 Jul 09 '22 at 15:33
7 Answers
At time of writing the other TeX based answers on this page are flawed in that they hide a conditional \ifx inside a macro but still use \else/\fi at the "top" level. This will mean that things break unexpectedly when used inside other conditionals.
The LaTeX3 programming language expl3 contains a module for doing stuff with token registers:
\usepackage{expl3}
...
\ExplSyntaxOn
\toks_if_empty:NTF \mytoks {true} {false}
\ExplSyntaxOff
It essentially does internally what the other answers here are suggesting, but it uses expansion to grab its arguments so the branching is robust (and you don't have \fi lying around to get in your way).
Update: So what does this approach do that is superior to other methods? Consider the style of solution first offered in answer to this question:
\def\IfEmpty#1{%
\edef\1{\the#1}
\ifx\1\empty
}
...
\IfEmpty\foo T\else F\fi
This doesn't behave nicely when nested, because TeX scans ahead when discarding unfollowed branches of a conditional. Consider
\ifx\bar\baz
\IfEmpty\foo T\else F\fi % <- uh oh
\else
E
\fi
If \bar = \baz, then the second branch is discarded and the first branch is executed. So far so good. If \bar ≠ \baz, then the first branch is discarded by reading ahead until the first unmatched \else — and this is the one in the line labelled "uh oh" above. So you could collapse the expansion of the above snippet in this case to:
\iffalse\else F\fi % <- uh oh
\else
E
\fi
and hence the cause of the ‘Extra \else’ error message in this case.
So this form for conditionals doesn't work so well. Next try. You can also write this style of code like this:
\def\IfEmpty#1#2#3{%
\edef\1{\the#1}
\ifx\1\empty
#2%
\else
#3%
\fi
}
This avoids the problems of nesting as in the previous trial solution, but it's prone to another problem: #2 and #3 have trailing material behind them, namely \else and \fi. This is a problem if you want to write something like
\def\processfoo#1{...something with #1...}
\IfEmpty\foo{\error}{\processfoo} {arg}
because the #1 passed to \processfoo will be \fi instead of the desired {arg}. The conditional in this case is better written as
\def\IfEmpty#1#2#3{%
\edef\1{\the#1}
\ifx\1\empty
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{#2}{#3}
}
so overcome this problem. This is how expl3 conditionals work, and it's why we're writing TF at the end of all their names to indicate "true" and "false" branches. (Or just T or just F if you only want one of them.)
Incidentally, there are expandable tests for checking for emptiness, which is why I suggest using the expl3 approach for this test. Expandability is not always required, of course, but code that is fully expandable tends to be more reliable and it's always nice to have for cases such as
\typeout{ \toks_if_empty:NT \foo {Warning:~\string\foo\space is~ empty} }
- 259,911
- 34
- 706
- 1,036
- 73,872
-
Did my updated answer fix the problems? If so, is there a way to do it that doesn't involves a sentinel? – TH. Sep 10 '10 at 05:42
-
Yes, it did. That's essentially the definition of the expl3 version, although the "emptiness" test in expl3 is slightly more robust. I'm not exactly sure what you mean specifically and exactly by "sentinel", but I think the answer is no. – Will Robertson Sep 10 '10 at 07:06
-
2I like such examples in expl3 syntax (and also the one in luatex) very much. It help to get used to their look and feel. E.g. I was quite confused about the \space behind \foo. The idea that \foo swallows a ~ like it swallows normally a space is - well - curious. – Ulrike Fischer Sep 10 '10 at 08:11
-
1@Ulrike: inside
\ExplSyntaxOn, tilde has the catcode of a space because space has the catcode of "ignored". If that's not to your liking, you can use\ExplSyntaxNamesOn, which is the same as\ExplSyntaxOnbut leaves spaces as-is. We should perhaps be promoting this more for use in general package code, since I guess the space-ignore-rule is probably somewhat inconvenient for integrating some expl3 code into regular LaTeX code. – Will Robertson Sep 10 '10 at 10:33 -
Another way of working around the problem with
\if-\fimatching is to design the macro so that it can be used like this:\ifnum0=\boolempty\toksreg true \else \false \fi. Here\boolemptyshould expand to0or1. It may be useful if you don't want to set the catcodes inside the branches too early, for example. It can be seen as the symmetric of your proposal: instead of putting the\fiinto the macro, remove the\iffrom it. – mpg Oct 28 '10 at 13:40 -
3@TH. There is a way which doesn't involve a sentinel (sort of), using
\detokenize. It begins like\expandafter\ifx\expandafter\notsentinel\detokenize\expandafter{\the#1}\notsentinel. Here,\notsentinelis used as a sentinel, but thanks to\detokenizethere is not risk that it accidentally matches the contents of the toks register tested. – mpg Oct 28 '10 at 13:44 -
@mpg: Ah, thanks. I think I need to look into the capabilities of e-TeX more. I tend not to use them for some reason. – TH. Oct 28 '10 at 20:29
-
Could you explain your method a little more fully for those of us who find it mysterious? Although I'm sure that your method is what I want to do, I'm sorry to say that I don't actually understand what code I should use to test, for example, if \toks0 is empty or not. – JDH Jan 13 '14 at 15:17
-
@JDH — expl3 no longer has constructs for toks registers, so this answer doesn't specifically address the original question. I think if you look through
expl3.pdfandsource3.pdfyou should be able to pick up the syntax fairly easily. – Will Robertson Feb 02 '14 at 11:12 -
@WillRobertson (I stumbled over the question yesterday and thus am late to the party.) Nowadays (January 10, 2022) expl3 has the construct
\tl_if_empty:VTFwhich currently can also be used with\toksdef-tokens denoting token-registers. All non Lua-approaches presented so far have the pitfall of failing in case ot the token-register to test containing\outer-tokens (which can easily be accomplished by redefining tokens in terms of \outer after placing them into the register). – Ulrich Diez Jan 10 '22 at 12:56
With LuaTeX you can do this:
\directlua{
if string.len(tex.toks["headline"]) > 0 then
print("not emtpy")
end
}
But since I never use token list: don't use this code to build nuclear plants.
- 37,020
-
-
If it doesn't work, I'd like to get a comment about what is wrong. My tests were all okay. – topskip Sep 10 '10 at 07:02
-
3I was rash. Using Lua to solve a TeX problem seems a bit batty to me (as in, why not skip using toks entirely and just do everything in Lua?). But at least the Lua code here is nice and straightforward, unlike the TeX code required. – Will Robertson Sep 10 '10 at 07:02
-
11We definitely need more answers of this kind. Even if it is solvable in TeX, a solution that mere humans can understand is highly desirable. (heck, even I understood it, unlike the other black expansion magic). – خالد حسني Sep 10 '10 at 07:20
Ulrich Diez regularly posts on comp.text.tex some code along the following lines (slightly modified by me for toks):
\newcommand\@ifempty@toks[1]{%
\ifcat\relax\detokenize\expandafter{\the#1}\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
Or, without using e-TeX,
\newcommand\@ifempty@toks[1]{%
\ifcase\iffalse{{{\fi\expandafter\@ifempty@@toks\the#1}1}1}0
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\newcommand{\@ifempty@@toks}
{\expandafter\@gobble\expandafter{\expandafter{%
\ifcase`}\expandafter}\expandafter\fi\string}
EDIT: Ulrich Diez rightfully points out that redefining \relax can make the first test fail. It is slightly safer to use a character token like $ or X that has category code among 3,4,7,8,11.
Additionally, neither method works if the \toks contains outer macros, as in \outer\def\foo{} \newtoks\mytoks \mytoks=\expandafter{\noexpand\foo}.
- 44,937
-
The non e-TeX version is an excellent example of using \ifcase to 'bury' the extra braces. It is missing two \expandafter's however: one before '`' and one after it (in the definition of @ifempty@@toks). I think it would also be nice if standard \def' were used instead of \LaTeX lingo. Thanks for the idea, anyway! – alexsh Mar 10 '14 at 02:56
-
Another comment is that to make it possible to use this macro inside alignments,
\}should be replaced by1\expandafter}` (see The TeXbook, p. 385 for the reasons and the discussion of the balance and master counters). In any case, this is a beautiful macro! – alexsh Mar 10 '14 at 18:50 -
1@alexsh The two
\expandafteryou suggest adding would do nothing. When TeX looks for a number after\ifcase, it reads the next two tokens, which denote the character code of}. It turns out that such a number can be followed by an optional space token, so TeX continues expanding what follows (namely\expandafter}\expandafter...) before using the number (character code of}) as the argument of\ifcase. – Bruno Le Floch Mar 12 '14 at 13:25 -
As far as I can tell without testing, your second comment is correct: to make the macro useable inside alignments, one should write
\ifcase1\expandafter}\expandafter}\expandafter\fi. – Bruno Le Floch Mar 12 '14 at 13:27 -
I stand corrected. I just assumed that
and – alexsh Mar 12 '14 at 19:52both disallowed an optional trailing space. Only the former one does, however, so the expansion will continue ... -
1Instead of
\ifcat\relax\detokenize\expandafter{\the#1}\relaxI suggest\ifcat$\detokenize\expandafter{\the#1}$in order not to rely on\relaxnot being redefined to fool the test as would be the case, e.g., with\def\relax{ZZ}. (Instead of$you can use some other character token not of category 12(other) or 10(space) or 13(active). ) A drawback of all these approaches is processing the result of\the<token-register>by means of macros: This fails if the token-register contains tokens defined in terms of\outer(after placing them into the register). – Ulrich Diez Jan 10 '22 at 13:21 -
-
@BrunoLeFloch Instead of
\outer\def\foo{} \newtoks\mytoks \mytoks=\expandafter{\noexpand\foo}you can do\newtoks\mytoks \mytoks={\foo} \outer\def\foo{}in order to create the situation of having\the\mytoksproduce an\outer-token ... :-)\detokenizedelivers spaces of category 10. Thus besides character-tokens of category 12 characters of category 10 should be avoided as well. Same for active characters as these might expand to something likeZZ. – Ulrich Diez Jan 11 '22 at 15:56 -
Correct. Same for catcode 1 or 2 which would make unbalanced things, and catcode 6, which is a mess to get into definitions. Overall that gives the options 3,4,7,8,11 so I've documented that now. – Bruno Le Floch Jan 12 '22 at 16:50
Would something like this work?
\def\ifempty#1{%
\edef\ContentsOfList{\the#1}%
\edef\CompareTo{}%
\ifx\ContentsOfList\CompareTo}
You could then use it like this:
\toks0={\blah\bloh a b c}
\toks2={}
\ifempty{\toks0} empty\else nonempty\fi
\ifempty{\toks2} empty\else nonempty\fi
- 19,242
-
And
\theinside\edefinserts the token list without expanding it. Yes, it works, I should have come up with that by myself. – Charles Stewart Sep 09 '10 at 17:59 -
1This does have the disadvantage that it isn't expandable. I.e., you couldn't use it like
\edef\foo{\ifempty{\toks0} \foo\else\bar}– TH. Sep 09 '10 at 18:02 -
@TH: To solve this without assignment do something like \expandafter\helper\the#1 \sentinel\anothersentinel and then make \helper do whatever it takes, namely pick something out of the expanded #1. This probably means \helper has to expand to \expandafter\anotherhelper\string, and now it's getting complicated. And perhaps can't be reliably done, however clever we are with sentinel. – Jonathan Fine Sep 09 '10 at 20:50
-
@Jan: We expect \ifx\a\b \ifempty{#1} \fi \fi to expand to nothing if the \ifx is false. But your definition does not have this property. – Jonathan Fine Sep 09 '10 at 20:56
-
Continuing JF's last comment, I do not recommend defining conditionals in this way. It will blow up in your face when you least expect it, usually when you try and nest this conditional within another. – Will Robertson Sep 09 '10 at 23:03
-
OK, how about this:
\def\DoIfEmpty#1#2#3{\edef\ContentsOfList{\the#1}\edef\CompareTo{}\ifx\ContentsOfList\CompareTo#2\else#3\fi}Would this be better? Is there a good way how to define new tests that could be used as conditionals?
– Jan Hlavacek Sep 10 '10 at 02:29 -
@Jonathan Fine: Does my (updated) solution not work (modulo the sentinel)? – TH. Sep 10 '10 at 04:12
-
That would work, with the caveat that it's not expandable and anything in #2 or #3 have to be careful that they will be followed by a \fi or \else term. Please see my answer below for more information. – Will Robertson Sep 10 '10 at 04:30
-
On the one hand
\ifemptyneeds to be trailed by\elseand/or\fifor matching up the\ifxdelivered by expanding\ifempty. On the other hand\ifemptyitself is not an\if..-switch but a macro which, if itself occurring within a branch of an\if..\else..\fi-expression that is skipped, does not match up the\elseand\fiby which it should be trailed. | Never give a macro a name that begins with\if..: When nesting/mixing with real\if..-switches there will be confusion about which tokens match up trailing\else/\fiand which don't. – Ulrich Diez Jan 11 '22 at 11:19 -
I suggest:
\long\def\fot#1#2{#1}\long\def\sot#1#2{#2}\def\CheckWhetherEmpty#1{\edef\ContentsOfList{\the#1}\def\CompareTo{}\ifx\ContentsOfList\CompareTo\expandafter\fot\else\expandafter\sot\fi}\toks0={\blah\bloh a b c}\toks2={}\CheckWhetherEmpty{\toks0}{empty}{nonempty}\CheckWhetherEmpty{\toks2}{empty}{nonempty}With this approach you loose independency of brace-nesting from\if..\else..\fi-nesting. Therefore an expandable approach is prefereable where you could do\if0\ExpandableCheckWhetherEmpty{\toks0}{0}{1}<true-branch>\else<false-branch>\fi– Ulrich Diez Jan 11 '22 at 11:24
Similar to Jan Hlavacek's answer, but expandable:
\def\certainlynotintoks{\certainlynotintoks}
\def\iftoksempty#1{\expandafter\ifx\expandafter
\certainlynotintoks\the#1 \certainlynotintoks}
\toks0{\undefined will not be expanded}
\toks2{}
\iftoksempty{\toks0} empty\else not empty\fi \par
\iftoksempty{\toks2} empty\else not empty\fi
This requires that \certainlynotintoks does not appear as the first token in the token register. Both this and Jan's answer do expand the token register, but only once. I don't believe there's a way to avoid that.
Edit: Taking Will's (absolutely correct) comment into consideration, change the definition of \iftoksempty to
\makeatletter
\def\iftoksempty#1{\expandafter\ifx\expandafter
\certainlynotintoks\the#1 \certainlynotintoks \expandafter\@firstoftwo
\else \expandafter\@secondoftwo\fi}
Then you can use \iftoksempty{\toks0}{empty}{not empty}.
- 62,639
-
In fact,
\the\toksregsimply inserts the token list without any expansion at all. – Charles Stewart Sep 09 '10 at 18:04 -
Yes, that's what I meant by expansion.
\the\toksregexpands to whatever that token register contains. – TH. Sep 09 '10 at 18:06 -
Again, hiding \ifx in a macro has dire consequences. It breaks nesting because TeX can't match its trailing \else...\fi with a leading \iftrue/\iffalse-equivalent conditional. – Will Robertson Sep 09 '10 at 23:07
-
-
What about the token-register in question containing unbalanced/unmatched
\ifor\elseor\fi? – Ulrich Diez Jan 10 '22 at 13:27
A rather retro way of avoiding the problems Will talks about is to use flags. Define:
\newif\iftest
\def\testtoksempty#1{\edef\1{\the#1}
\ifx \1\empty \testtrue % from plain.tex: \def\empty{}
\else\testfalse \fi}
Then use:
\testtoksempty\toksreg
\iftest toksreg is empty \else toksreg's got something \fi
I tend to avoid any kind of overlap between \if conditional and macro expansion because of my incomplete, fearful grasp of the issues Will nicely explained (and which I now understand a bit better).
This is clunky, ASM-like way to code, and the use of a fixed test flag means that Boolean expressions are going to be fiddly: you may be driven to multiple, nearly identical macros - \tessttoksempty, \tesssttoksempty, etc.
But it's robust and easy to debug.
- 119,821
- 21,014
- 5
- 65
- 121
With all "traditional" approaches presented so far and with expl3's \tl_if_empty:VTF the content of the token-register is produced via \the and examined by means of macros.
The circumstance of a macro-argument containing \outer-tokens triggers an error-message.
Therefore these "traditional" approaches and expl3's \tl_if_empty:VTF fail if the register to test for emptiness contains tokens that at the time when being produced by \the are defined in terms of \outer.
E.g., with
\newtoks\mytoks
\mytoks{\foo}%
\outer\def\foo{foo is outer}%
\the\mytoks will produce a token which is defined in terms of \outer and therefore cannot be a component of a macro-argument.
(However, interface3.pdf, the reference documentation for the expl3 programming environment, explicitly states in section "1.4 TeX concepts not supported by LaTeX3":
The TeX concept of an "
\outer" macro is not supported at all by LaTeX3. As such, the functions provided here may break when used on top of LaTeX 2ε if\outertokens are used in the arguments.
)
If working on a LuaLaTeX-engine, according to LuaTeX Reference Manual stable December 2021 Version 1.15, you can do something like this, where \outer-tokens are not a problem:
\errorcontextlines=10000
\makeatletter
%%=============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\@ifdefinable\gobbletoks{%
\begingroup
\escapechar=-1
\expandafter\endgroup\expandafter\def\expandafter\gobbletoks\expandafter#\expandafter1\string\toks{}%
}%
\newcommand\toksnum[1]{\expandafter\gobbletoks\meaning#1}%
% Don't use tex.sprint or the like for sending control-sequence-tokens to TeX-level
% as tex.sprint("\macro") only works out as long as backslash has catcode 0.
\newcommand\CheckWhetherTokenRegisterEmpty[1]{%
\romannumeral
\directlua{%
if string.len(tex.toks[\number\toksnum#1]) > 0 then
token.put_next ( token.create("@secondoftwo") ) else token.put_next ( token.create("@firstoftwo") )
end
token.put_next ( token.create("expandafter"), token.create("UD@stopromannumeral") )
}%
}%
%%=============================================================================
\makeatother
\newtoks\testtoks
\testtoks={}
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
\testtoks={ }
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
\testtoks={something}
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
% This is not a problem:
\testtoks={\foobar}
\outer\def\foobar{foobar is outer}
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
\catcode\/=0 \catcode\=12
/message{%
^^J%
/string/testtoks
/CheckWhetherTokenRegisterEmpty/testtoks{ is}{ is not} empty%
}%
/catcode/\=0 \catcode//=12
\stop
If Lua-extensions are not available and if you can rely on the content of the token-register not containing \outer-tokens you can probably do something like this:
\errorcontextlines=10000
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo,\UD@stopromannumeral, \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@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/g/comp.text.tex/c/kuOEIQIrElc/m/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}%
}%
%%-----------------------------------------------------------------------------
%% With eTeX do something like:
%%
%% \newcommand\UD@CheckWhetherNull[1]{%
%% \romannumeral\ifcat$\detokenize{#1}$%
%% \expandafter\expandafter\expandafter\UD@stopromannumeral
%% \expandafter\UD@firstoftwo
%% \else
%% \expandafter\expandafter\expandafter\UD@stopromannumeral
%% \expandafter\UD@secondoftwo
%% \fi
%% }%
%%-----------------------------------------------------------------------------
%% Check whether token-register is empty:
%%.............................................................................
\newcommand\CheckWhetherTokenRegisterEmpty[1]{%
\romannumeral
\expandafter\expandafter\expandafter\UD@secondoftwo
\expandafter\UD@CheckWhetherNull
\expandafter{\the#1}%
}%
%%=============================================================================
\makeatother
\newtoks\testtoks
\testtoks={}
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
\testtoks={ }
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
\testtoks={something}
\message{%
^^J%
\string\testtoks
\CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
}%
% This is a problem:
%
% \testtoks={\foobar}
% \outer\def\foobar{foobar is outer}
% \message{%
% ^^J%
% \string\testtoks
% \CheckWhetherTokenRegisterEmpty\testtoks{ is}{ is not} empty%
% }%
\stop
Another interesting question might be the handling of the case of the argument of the routine for checking emptiness of a token-register not denoting a token-register at all.
But the question of how to implement a test for checking if an arbitrary given argument denotes a token-register is a different question and thus is out of the scope of this question.
Still, a few thoughts on this that get one used to the idea that TeX may not allow you to implement every testing-routine one hundred percent accurately and reliably:
In case of the argument denoting a token-register the argument might consist of
- a single
\toksdef-token, \toks⟨s.th. that expands to a TeX-⟨number⟩-quantity with a value in range of available token-registers⟩.
So a check would imply handling the case of the first token of the argument being the \toks-primitive in which case you would need to check if (recursive) expansion of following tokens yields only a valid TeX-⟨number⟩-quantity. As following tokens might form an expansion-based algorithm, the check would imply the task of checking if an arbitrary expansion-based algorithm terminates at all, and if so, if termination goes along with only delivering a valid TeX-⟨number⟩-quantity. Checking if an arbitrary expansion-based algorithm terminates at all reminds me of the halting-problem.
- 7,143
- 28,770