0

I'm using TeXShop 5.0.4 and encountering a cryptic error

! Incomplete \iffalse; all text was ignored after line 15.
<inserted text>
               \fi

where my code contains no explicit "if"s.

How can I create a loop that defines various commands, each of which contains its own loop?

Explanations and solutions greatly appreciated.

Small repro:

\documentclass[11pt]{article}
\usepackage{tikz}

% given input x, define \allx as a function that consumes input list qlist and operates on the elements of qlist (here, prints "x: q", but eventually some tikz command) % This errors out with the comprehensive error % ! Incomplete \iffalse; all text was ignored after line 15. % <inserted text> % \fi \newcommand{\allcolor}[1]{\expandafter\xdef\csname all#1\endcsname##1{\foreach \q in {##1} {#1: \q}}} % Interestingly, the \foreach seems to cause the error. If using the command below instead, which just prints "x: qlist", LaTeX is happy. %\newcommand{\allcolor}[1]{\expandafter\xdef\csname all#1\endcsname##1{#1: ##1}}

% Reasons for pieces of the above code: % use \xdef instead of edef to make the new function global, since \allcolor will be called in a loop, from with edef would only define the function within the loop body % use \xdef instead of \newcommand because of other cryptic errors with \newcommand % use \expandafter on \xdef since we first need to form the name of the new function, "\all#1", via \csname

% given input list {x,y,z}, call \allcolor on each one, to define \allx \ally and \allz \newcommand{\allcolors}[1]{\foreach \color in {#1} {\allcolor{\color}}}

\allcolors{orange}

\begin{document} \allorange{ha,haa} \end{document}

Edit: the eventual goal is to define \allorange, \allbrown, (with the ability to easily add other color commands), that each process a list of coordinate pairs, and plot points of that color within tikz.

Using egreg's answer, I was able to do so, with the following code:

\documentclass[11pt]{article}
\usepackage{tikz}

\edef\getxnow(#1,#2){#1} \edef\getynow(#1,#2){#2} \newcommand{\getx}[1]{\expandafter\getxnow#1} \newcommand{\gety}[1]{\expandafter\getynow#1}

\newcommand{\allcolor}[1]{% \expandafter\gdef\csname all#1\endcsname##1{% \foreach \pt in {##1} {\node at (\getx{\pt}, \gety{\pt})[circle, fill, inner sep=5pt, #1]{};}% }% } \newcommand{\allcolors}[1]{\foreach \r in {#1} {\expandafter\allcolor\expandafter{\r}}}

% Define \allorange and \allbrown which create points of those colors \allcolors{orange, brown}

\begin{document}

\begin{tikzpicture} { \allorange{(0,1),(1,2),(2,3)} \allbrown{(1,1),(2,2),(3,3)} } \end{tikzpicture}

\end{document}

Notes for commenters:

  • In the original code, I did not think anything of my local loop variable name \color - it was an arbitrary name, and any other name will do
  • It seems that \xdef tends to break stuff, while \gdef ends up working. I can see from What are the differences between \def, \edef, \gdef and \xdef? that \xdef = \global\edef expands replacement text at definition time, while gdef = \global\def doesn't. I'm not familiar of the inner workings of TeX replacement, so an explanation of why one succeeds while the other fails would help casual users' understanding.

Resources consulted:

1 Answers1

0

It's not clear what your macros are supposed to achieve, but the main problem is that \foreach cannot be used in \xdef.

With the following code

\documentclass[11pt]{article}
\usepackage{tikz}

\newcommand{\allcolor}[1]{% \expandafter\gdef\csname all#1\endcsname##1{% \foreach \q in {##1} {#1: \q\par}% }% }

% given input list {x,y,z}, call \allcolor on each one, to define \allx \ally and \allz \newcommand{\allcolors}[1]{\foreach \r in {#1} {\expandafter\allcolor\expandafter{\r}}}

\allcolors{orange}

\begin{document}

\allorange{ha,haa}

\end{document}

(added \par for clarity of output) I get

enter image description here

The command \allorange has replacement text

\foreach \q in {#1} {orange: \q \par }
egreg
  • 1,121,712
  • This worked, and I updated the Question with a working version of my eventual goal based on this. Thanks! – David Fink Nov 26 '22 at 04:18
  • Technically you could do like \protect\foreach or \noexpand\foreach, but indeed \gdef is easier in this case. – user202729 Nov 26 '22 at 05:20
  • @user202729 the \protect mechanism will not work in a normal \xdef, if you want to use that mechanism one has to use \protected@xdef instead. – Skillmon Nov 26 '22 at 15:52
  • @egreg Why are the \expandafter calls within the \allcolors definition necessary?

    @user202729 and @Skillmon can you explain how + why \protect or \protected@xdef would help here?

    – David Fink Nov 27 '22 at 18:27
  • Found https://tex.stackexchange.com/questions/345899/how-does-expandafter-interact-with : \expandafter operates on the { token, so \newcommand{\allcolors}[1]{\foreach \r in {#1} {\expandafter\allcolor\expandafter{\r}}} \allcolors{x,y} expands the token following \allcolor before it, which is another expandafter: expanding the \r to each loop iteration before the { expands (to itself), then finally evaluating allcolor{x} and allcolor{y} (including the necessary curly braces).

    Nested macros + expansion timing are quite counter-intuitive for C/C++ programmers...

    – David Fink Nov 27 '22 at 19:17