There is more than one problem with such a definition. The \def
instruction has the following syntax:
\def<cs><parameter text><left brace><balanced text><right brace>
where <cs>
is the control sequence or active character to define; <left brace>
and <right brace>
stand for explicit braces (character tokens with category code 1 and 2, respectively); <balanced text>
is any token list with balanced explicit braces, which can contain any #x
(x
a digit from 1 to 9) that appear also in <parameter text>
.
The important thing here is of course the <parameter text>
, which can be quite general since the only limitations are essentially that explicit braces are not allowed in it and the "parameter tokens" #1
, #2
and so on must appear in order.
If the parameter text starts with something different from #1
, those tokens are required to follow the macro name. If a parameter token is immediately followed by another parameter token it represents an undelimited argument, otherwise it represents a delimited token and those tokens must appear in the same order when the macro is called: control sequence names must be the same and, for characters, both the character code and category code must match. The argument will be all that goes from the preceding delimiter (or argument if it was undelimited) up to the delimiter token, but that also results in a token list that is balanced with respect to braces.
For instance, let's look at
\def\foo\bar#1x\baz#2#3\end{#1--#2--#3}
Here we have
a "macro delimiter" \bar
the first argument is anything that goes from \bar
(excluded) to the first instance of x\baz
the second argument is "undelimited", so it will be the next token or, when this is a left brace, all the balanced group
the third argument is anything from the end of the second argument to \end
Examples of calls
\foo\bar XYx\baz ZABC\end
We have #1=XY
, #2=Z
, #3=ABC
\foo\bar XYx\baz {SOME}ABC\end
We have #1=XY
, #2=SOME
, #3=ABC
(the braces are stripped off the undelimited argument)
\foo\bar {XYx\baz}x\baz ZABC\end
We have #1=XYx\baz
, #2=Z
, #3=ABC
, because the first x\baz
pair is at brace level one
\foo\bar XYx\baz Z\end
We have #1=XY
, #2=Z
, and #3
is empty.
Many other examples can be concocted, but it's best to refer to the TeXbook (chapter 20) or TeX by Topic (chapter 11)
Let's see what happens with the code you gave
\def\drawconnectvertices[num vertex=#1, circle radius=#2] at (#3);{...}
(the <replacement text>
is irrelevant for this discussion).
The parameter text consists of a "macro delimiter":
[num vertex=
The first argument is delimited by
, circle radius=
The second argument is delimited by
] at (
The third argument is delimited by
);
Thus a call like
\drawconnectvertices[num vertex=6, circle radius=3] at (0,0);
will absorb
#1 = 6
#2 = 3
#3 = 0,0
The delimiter tokens will be discarded in the process.
What can go wrong?
In option lists it's common to omit spaces after a comma; so a user might input
\drawconnectvertices[num vertex=6,circle radius=3] at (0,0);
which would result in TeX not finding the correct delimiter for the first argument and continue reading from the file until the end. Well, not until the end of the file, because some particular spots will tell TeX that something went wrong, in particular a \par
token will. The error message would be, in this case,
! Paragraph ended before \drawconnectvertices was complete.
<to be read again>
\par
which doesn't seem very helpful.
The same would happen with
\drawconnectvertices[num vertex=6, circle radius=3] at (0,0) ;
because of the space in front of the semicolon; this might be even more disastrous, because some other later TikZ instruction could have the form (1,2);
and all the text from 0,0
up to 1,2
would be considered the third argument to \drawconnectvertices
.
How to do it better?
Use the proper TikZ conventions for defining such a macro: define keys and use the infrastructure provided by PGF/TikZ (I'm no expert in this and surely someone else will help in finding a more robust definition). Surely a safe definition would be:
\def\drawconnectvertices#1#2#3{%
\pgfmathtruncatemacro\vertices{#1}
\pgfmathsetmacro\circleradius{#2}
\pgfmathsetmacro\halfcircleradius{\circleradius/2}
\draw[blue] (#3) circle (\halfcircleradius cm) node[regular polygon, regular polygon sides=\vertices, minimum size=\circleradius cm, draw=none, name={vertex set}] {};
\foreach \x in {1,...,\vertices}{
\node[draw,circle, inner sep=1pt,blue, fill=blue] at (vertex set.corner \x) {};
}
\foreach \x in {1,...,\vertices}{
\foreach \y in {\x,...,\vertices}{
\draw[ultra thin, red] (vertex set.corner \x)--(vertex set.corner \y);
}
}
}
with a call such as
\drawconnectvertices{6}{3}{0,0}
but, of course, a more TikZish version would be more attractive and also clearer.
A more TikZish markup
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,shapes.geometric}
\makeatletter
\tikzset{
num vertices/.code=\def\dcv@vertices{#1},
num vertices/.default=3,
circle radius/.code=\def\dcv@circleradius{#1},
circle radius/.default=1,
}
\newcommand\drawconnectvertices[1][]{%
\tikzset{num vertices, circle radius, #1}%
\dcv@auxi}
\def\dcv@auxi#1at#2;{\dcv@auxii#2;}
\def\dcv@auxii#1(#2)#3;{\def\dcv@origin{#2}\dcv@main}
\def\dcv@main{%
\pgfmathtruncatemacro\dcv@vertices{\dcv@vertices}
\pgfmathsetmacro\dcv@circleradius{\dcv@circleradius}
\pgfmathsetmacro\dcv@halfcircleradius{\dcv@circleradius/2}
\draw[blue] (\dcv@origin) circle (\dcv@halfcircleradius cm)
node[regular polygon,
regular polygon sides=\dcv@vertices,
minimum size=\dcv@circleradius cm,
draw=none,
name={vertex set}] {};
\foreach \x in {1,...,\dcv@vertices}{
\node[draw,circle, inner sep=1pt,blue, fill=blue] at (vertex set.corner \x) {};
}
\foreach \x in {1,...,\dcv@vertices}{
\foreach \y in {\x,...,\dcv@vertices}{
\draw[ultra thin, red] (vertex set.corner \x)--(vertex set.corner \y);
}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\drawconnectvertices[num vertices=6,circle radius=3] at (0,0);
\end{tikzpicture}
\begin{tikzpicture}
\drawconnectvertices[circle radius=3] at (0,0);
\end{tikzpicture}
\begin{tikzpicture}
\drawconnectvertices[num vertices=6] at (0,0);
\end{tikzpicture}
\end{document}

I'm sure that TikZ has features for making this even more robust.
circle
, for instance, you'll get a puzzling error.\latex/
so you don't have to think about following spaces.tikzlibrarytrees.code.tex
:\def\tikz@parse@three one child at#1(#2)#3and two children at#4(#5)#6and#7(#8){%
.#1
,#3
,#4
,#5
and#7
are only there to allow for having spaces betweenat
,and
and(
or)
or a tighter input likeat(
. The actual arguments that get processed are#2
,#5
and#8
. The same way how TikZ works his way through a path.