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:

8
  • most latex constructs will fail in an xdef, certainly \foreach does assignments so can not possibly work. Nov 25, 2022 at 20:02
  • also \foreach \color will over-write the standard \color command, Can you say in words what you want \allcolors{orange} and \allorange{ha,haa} to do? Nov 25, 2022 at 20:05
  • @DavidCarlisle but as long as the standard \color is not needed inside the \foreach-code it'll not hurt because of the implicit groups. Though it is still a bad idea.
    – Skillmon
    Nov 25, 2022 at 20:24
  • It seems like you want to use \gdef instead of \xdef.
    – Skillmon
    Nov 25, 2022 at 20:25
  • @Skillmon sure but i couldn't guess what this code was intended to do. Nov 25, 2022 at 20:51

1 Answer 1

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 }
5
  • This worked, and I updated the Question with a working version of my eventual goal based on this. Thanks!
    – David Fink
    Nov 26, 2022 at 4:18
  • Technically you could do like \protect\foreach or \noexpand\foreach, but indeed \gdef is easier in this case.
    – user202729
    Nov 26, 2022 at 5: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, 2022 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, 2022 at 18:27
  • Found tex.stackexchange.com/questions/345899/… : \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, 2022 at 19:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .