An example from the TikZ manual re the /tikz/graph/parse key

I will list two LaTeX manuscripts. The first one, which is the example given in the manual to demonstrate the usefulness of the /tikz/graph/parse key, compiles successfully, whereas the second one fails to compile. After listing the first manuscript and showing its output, I will argue that the second manuscript is equivalent to the first one. Clearly this argument is false, but I don't know why.

Consider the following LaTeX manuscript, which is essentially a copy of the example in the end of subsection 19.3.2 ('Syntax of Group Specifications') of section 19.3 ('Syntax of the Graph Path Command') of chapter 19 ('Specifying Graphs') of the TikZ & PGF manual for version 3.0.1a (p. 265).

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{graphs}
\begin{document}
\def\mychain#1{
\def \mytext{1}
\foreach \i in {2,...,#1} {
\xdef\mytext{\mytext -> \i}
}
}
\tikzgraphsset{my chain/.style={
/utils/exec=\mychain{#1},
parse/.expand once=\mytext}
}
\tikz \graph { [my chain=4] };
\end{document}


The resulting rendered picture is (not to scale)

I'll explain the output as best I understand it.

1. The \tikzgraphsset command simply executes its keys with the path prefix /tikz/graphs (the \tikzgraphsset command is described in p. 262). So the key /tikz/graphs/my chain becomes an abbreviation that expands to /utils/exec=\mychain{#1}, parse/.expand once=\mytext.
2. Next the \graph ... command is executed. The only thing to do is to process the group options ([my chain=4]). The group options are executed with the path prefix /tikz/graphs (this is described in p. 264). So the key assignment /tikz/graphs/mychain=4 is executed. This expands to /utils/exec=\mychain{4},/tikz/graphs/parse/.expand once=\mytext.
3. Now the assignment /utils/exec=\mychain{4} is executed. This key simply executes the assigned value (this key is described in p. 893 in the 'Key Management' chapter), so the macro \mychain{4} gets expanded.
4. As the last step, the assignment /tikz/graphs/parse/.expand once=\mytext is executed. The key handler .expand once causes \mytext's replacement text, namely 1 -> 2 -> 3 -> 4, to be assigned to /tikz/graphs/parse (the .expand once key handler is described in p. 890 in the chapter 'Key Management' chapter). The effect of assigning to the /tikz/graphs/parse key is that the assigned value is inserted at the beginning of the current group, as if you had entered it there (/tikz/graphs/parse is described in p. 265.)

In conclusion, the above manuscript should be equivalent to the following:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{graphs}
\begin{document}
\def \mychain#1{
\def \mytext{1}
\foreach \i in {2,...,#1} {
\xdef\mytext{\mytext -> \i}
}
}
\tikz \graph { \mychain{4} 1->2->3->4 };
\end{document}


And yet the last manuscript fails to compile, and pdftex reports the following error message:

ERROR: Undefined control sequence.

--- TeX said ---
\mychain #1-> \def \mytext
{1} \foreach \i in {2,...,#1} { \xdef \mytext {\m...l.11     \tikz \graph { \mychain{4} 1->2->3->4 }


I will appreciate it if someone can explain not only why the second example fails, but, more importantly, how the first example works. I'd like to be able to predict how similar examples will play out.

• Where is \mytext in the graph ? You just defined it Jul 30 '17 at 13:24
• The original example is expanding stuff and then inserting it in the relevant place. In your example, the stuff is just being inserted as replacement tokens, since that's what a macro does. But you can't use, say, \def\mytext{1} here in the argument. It works in the first case because stuff gets expanded first. If you define my chain in the first example as parse=\mychain{#1} rather than using exec, then you have something closer to your second example.
– cfr
Jul 30 '17 at 14:04
• it's not clear why you think the forms are equivalent. if you can do \tikz{...} that does not mean that you can do \tikz{\def\tmp{...}\tmp} Jul 30 '17 at 14:32
• graph parser changes the reading of the code. There are all kinds of differences inside the graph parser than TikZ which is also different than TeX completely. So it is perfectly normal if the parser is not expanding the macros on its way because it is constantly looking for characters to understand the context. The source is full of \pgfutil@ifnextchar conditionals. So the answer is just it's by design. Jul 30 '17 at 14:37
• That basically is why you are expecting the wrong result, you are not distinguishing expandable from non expandable operations. Your second form inserts extra \def that typically does not work \def\tmp{1}\tmp will typeset 1 but \setcounter{equation}{\def\tmp{1}\tmp} will not set the counter to 1. whereas \def\tmp{1}\setcounter{equation}{\tmp} will. the difference is all about where expansion occurs Jul 30 '17 at 15:53

Here's a rough outline* of how the TikZ "engine" processes the path

\graph { [my chain=4] };


Let's say the graph parser is called \graph@parser (it is not, this is just for the sake of illustration). Then the above statement expands to

{\graph@parser [my chain=4] };}


In other words, the two tokens \graph { were replaced by the \graph@parser control sequence, and the entire statement was then surrounded by a TeX group.

So now TeX opens a new scope, and proceeds to work on the following input stream:

\graph@parser [my chain=4] };}


When the parser sees the [, it replaces the entire [...] construct with \pgfkeys{/tikz/graphs/.cd,...}, and parsing resumes after execution of the options. In other words, the last code snippet expands to

\pgfkeys{/tikz/graphs/.cd,my chain=4}\graph@parser };}


The /tikz/graphs/.cd key signals that keys with a relative path (or 'partial path' to use the manual's terminology) are to be interpreted relative to the base path /tikz/graphs. So the last code snippet expands to

\pgfkeys{/tikz/graphs/my chain=4}\graph@parser };}


Since /tikz/graphs/my chain is a style, it is replaced by its "replacement text" after substituting the assigned value 4 for the parameter #1, so the last code snippet expands to

\pgfkeys{/utils/exec=\mychain{4},/tikz/graphs/parse/.expand once=\mytext}%
\graph@parser };}


As mentioned in the original post, this expands to

\mychain{4}\pgfkeys{/tikz/graphs/parse/.expand once=\mytext}\graph@parser };}


\mychain[4} expands ultimately to \gdef\mytext{1->2->3->4}. So the macro \mytext is saved in TeX's internal state, and the last code snippet reduces to

\pgfkeys{/tikz/graphs/parse/.expand once=\mytext}\graph@parser };}


As mentioned in the original post, this expands to

    \pgfkeys{/tikz/graphs/parse=1->2->3->4}\graph@parser };}


The action of the key /tikz/graphs/parse is to insert its argument just following the \graph@parser control sequence. So the last code snippet expands to

    \graph@parser 1->2->3->4};}


The overall effect of all these expansions is as though the original path (\graph { [my chain=4] };) had been replaced by

{%
\gdef\mytext{1->2->3->4}%
\graph{1->2->3->4}%
}


This, in turn, is equivalent to

\def\mytext{1->2->3->4}%
\graph{1->2->3->4}


* To be clear: this means that I'm not telling the full story, and that I'm telling some white lies to make the presentation clearer and cleaner.

• You've moved options out of the group to which they are restricted. If you write \graph -> \path graph. If you move the options out of the group, they apply to the whole path. But that's not how it works.
– cfr
Jul 30 '17 at 23:27
• @cfr: The full path for parse is /tikz/graphs/parse, so it would be included in <options 2> and after the re-write be part of the options passed to the \graph command. Jul 31 '17 at 2:53
• Missed that you said that above. But the main issue still remains. \path <bunch of stuff> graph [<options>] {<spec>} <another bunch of stuff>; has to make sense, because \graph just is \path graph. In your example, <options> is empty. The only options are part of the <spec>. But even if you had <options>, it still isn't equivalent to move then outside the scope of the graph. They don't apply to the rest of the path.
– cfr
Jul 31 '17 at 3:01
• Try e.g. \tikz \path [draw=blue] (0,0) -- (1,1) graph [edges=red] { [my chain=4] } graph [edges=<->, nodes={y=-1}] { 5 -- 6 -- 7 -- 8 } (8) edge [green, <-, thick] ++(0,-1) ;.
– cfr
Jul 31 '17 at 3:19
• Or \tikz \path [draw=blue] (0,0) -- (1,1) graph [edges=red] { [my chain=4] } graph [edges=<->, nodes={y=-1}] { [/tikz/orange] 5 -- 6 -- 7 -- 8 } (8) edge [green, <-, thick] ++(0,-1) ; and note what does and doesn't turn orange ....
– cfr
Jul 31 '17 at 3:42