0

The following example is largely shorn of its original context as part of the process of simplification.

This LaTeX file, if compiled with pdflatex parameternum.tex, gives

! Illegal parameter number in definition of \reserved@a.

The structure of this file has been the same for a long time. However, I only recently added the use of \IfFileExists and \PackageError to \foo, which made it blow up. If one just uses

\NewDocumentCommand{\foo}{m +m}
{
  #2
}

there is no error. Similarly, if one leaves out

\renewcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{\theletternum:#1}}

from the second argument in the call to \foo, the error disappears. These two changes are different, but they both involve a parameter character.

Alternatively, leaving in that line, but doubling the parameter character from # to ## also makes the error go away. I.e.

\renewcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{\theletternum:##1}}

This seems similar to the situation where defining a macro inside the definition of another macro requires doubling the parameter character, and the error is similar. But as far as I can tell, this is the (re)definition of a macro inside a macro invocation, so not the same thing.

Doubling the parameter character takes care of the error, as already said, but of course I'd like to know what is going on here.

Running a trace does not enlighten me, though it does produce

<argument> ...etabenum .\label {\theletternum :##1
                                                  }} Text
\reserved@a #1#2->#1

The file follows.

% 12pt default for scrlttr2
\documentclass{scrlttr2}
\newcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{#1}}

% \NewDocumentCommand{\foo}{m +m}
% {
%   #2
% }

\NewDocumentCommand{\foo}{m +m}
{
 \IfFileExists{./#1}{
   #2
 }{\PackageError{foo}{CONFIG FILE '#1' DOES NOT EXIST - NOT CREATING FORMLETTER}{Check that the config filename  (first argument) has been correctly passed to \formletter}}
}

% Parameters for document commands
\newcommand{\addressesconfigfilename}{formletterbug.addresses.yaml}

\begin{filecontents}[overwrite,noheader]{\addressesconfigfilename}
foo
\end{filecontents}

\begin{document}
\newcounter{letternum}
\foo{\addressesconfigfilename}
{
  \stepcounter{letternum}
  \renewcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{\theletternum:#1}}
  Text
}
\end{document}

PS. When I was posting this, "Illegal parameter number in definition of \reserved@a" following \IfFileExists came up as a similar question. Since this also involves \IfFileExists it's probably relevant. Though I don't understand Phelype's answer there.

\IfFileExists doesn't work like “usual” conditionals: it stores the true and false branches in macros, and uses them later.

More details, please. Does this correspond to defining a macro inside another macro? If not, why is the doubling necessary?

3
  • TeX itself has no difference between defining and redefining a macro, LaTeX just adds \newcommand and \renewcommand with appropriate checks around \def. So \renewcommand is at its core still \def, just like \newcommand.
    – Skillmon
    Commented Jun 21, 2023 at 13:49
  • 1
    The \IfFileExists internally uses something like \def\next{#2}\next (well, \expandafter\def\expandafter\reserved@a\expandafter{\reserved@a{#2}{#3}}\reserved@a, with \reserved@a being \@firstoftwo or \@secondoftwo). So you're doing \def\reserved@a{\renewcommand\nextnuml[1]{...#1}} and that errs.
    – Skillmon
    Commented Jun 21, 2023 at 13:54
  • You want ##1 in the replacement text of \nextnuml. And also to avoid lots of spurious spaces in your code. But you also want to expand \theletternum
    – egreg
    Commented Jun 21, 2023 at 13:56

1 Answer 1

4

You are passing

\stepcounter{letternum}
  \renewcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{\theletternum:#1}}
  Text

as argument into the \IfFileExists and as described in the other question it then chokes on the #1.

You can use an indirection instead:

\makeatletter
\NewDocumentCommand{\foo}{m +m}
{
 \IfFileExists{./#1}
  {\@firstoftwo}
  {\@secondoftwo}
  {#2}
  {\PackageError{foo}{CONFIG FILE '#1' DOES NOT EXIST - NOT CREATING FORMLETTER}{Check that the config filename  (first argument) has been correctly passed to \formletter}}
}

or use the L3 command:

\ExplSyntaxOn
\NewDocumentCommand{\foo}{m +m}
{
 \file_if_exist:nTF
  {./#1}
  {#2}
  {\PackageError{foo}{CONFIG~FILE~'#1'~DOES~NOT~EXIST}{}}
}
\ExplSyntaxOff
1
  • Thank you for the explanation. If I understand correctly, one can deal with this in an ad-hoc case by case basis by using ## everywhere that is necessary, but it's certainly better to use a version of \IfFileExists where one does not to deal with this. And I suppose the LaTeX 3 version is the simplest, because no modifications are necessary. Commented Jun 21, 2023 at 17:56

You must log in to answer this question.

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