2

I'm trying to write a macro that takes arguments containing paragraphs. If you write a normal macro and one of its arguments contains a paragraph, it will break:

\documentclass{article}
\usepackage[utf8]{inputenc}

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\def\mymacro#1{#1}

\mymacro{This

contains a paragraph}

\end{document}

This produces an error:

Runaway argument?
{This
! Paragraph ended before \mymacro was complete.
<to be read again> 
                   \par
l.15

So I tried to redefine \par for expanding the arguments of the macro, but now it won't stop compiling:

\documentclass{article}
\usepackage[utf8]{inputenc}

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\gdef\oldpar{\par}

\def\mymacro{\gdef\par{}\mymacroi}

\def\mymacroi#1{#1\gdef\par{\oldpar}}

\mymacro{foo

bar}

\end{document}
  • 6
    Use \long\def or \newcommand. – Phelype Oleinik Mar 7 at 13:11
  • 2
    I recommend defining a new environment instead of a command. – user156344 Mar 7 at 13:11
  • @PhelypeOleinik If you make that an answer, I'll accept it. I was so sure that I had written commands in the past whose arguments could span paragraphs. It makes sense now, it was before I started using \def instead of \newcommnad. – sgf Mar 7 at 13:13
  • \def is not a latex command, \newcommand which, is already allows paragraphs. – David Carlisle Mar 7 at 13:13
  • @DavidCarlisle Sometimes I need versions of \def. I have no idea how to replicate \edef, \gdef or \xdef with \newcommand (which I believe is \gdef?). What does it even mean for a tex command not to be a latex command? – sgf Mar 7 at 13:31
10

At the time TeX was written, one page of a document would take several minutes to be processed, and syntax highlighting was not a thing, so it was a good thing to have some mechanism to detect if you forgot a }. A \def, by default, doesn't allow a \par token unless you explicitly say it's a \long\def:

\def\mymacro#1{#1}

LaTeX, on the other hand, uses that by default, so if you use proper LaTeX commands (\def shouldn't be used in LaTeX documents), \newcommand makes a \long\def by default. If you want a “short” \def then you use \newcommand*.

xparse returns the short argument default, but lets you define a \long macro using the + argument modifier:

\NewDocumentCommand\mymacro{ m}{#1}% \def
\NewDocumentCommand\mymacro{+m}{#1}% \long\def

Your second attempt is clever, and it could have worked except for two things.

First is that you are using \gdef\oldpar{\par} and then \gdef\par{\oldpar}. Once you expand \par you get \oldpar which, when expanded, yields \par which, when expanded, yields \oldpar which, when expanded, yields \par which, when expanded, yields \oldpar... Running forever :/

You need to use \let (or \global\let to have global effect) in this case: \let\oldpar\par. This creates an exact copy of \par named \oldpar which does not depend on what is \par.

Second, the runaway argument checking is implemented in a lower level, independent of the definition of \par, so this would fail with the same error:

\let\par\relax
\def\mymacro#1{#1}
\mymacro{foo

bar}

because when TeX sees two \endlinechar tokens (which is a space by default) TeX inserts an implicit \par token, which raises the Runaway argument error. Knowing that, then:

\newcount\oldELchar
\oldELchar=\endlinechar
\def\mymacro{\endlinechar=-1\relax\mymacroi}
\def\mymacroi#1{#1\endlinechar=\oldELchar}
\mymacro{foo

bar}

won't raise an error, but a new line won't be a space anymore.

  • Haha, I was wondering where I went into infinite recursion... – sgf Mar 7 at 13:36
  • @sgf You can say, for instance, \tracingall before the command you suspect is infinite-looping. You'll get a ton of garbage in the terminal and the log, but it gets easier to find out what is TeX doing. – Phelype Oleinik Mar 7 at 13:39
  • I would take a slight difference with the statement "(\def shouldn't be used in LaTeX documents)". Certain service routines a user writes may require the special parsing provided by \def syntax. I think it would be better to say "(\def should be avoided, where possible)". – Steven B. Segletes Mar 7 at 13:44
  • 1
    the endlinechar version has to be used with care, for example it also removes the end of line after \mymacro (even if there is text following \mymacro) not just the one in the argument. – David Carlisle Mar 7 at 13:47
  • 1
    tex takes a line at a time and inserts the character specified by \endlinechar at the point it grabs the next line at the end of the buffer before tokenizing., so its timing is well defined but er "delicate" :-) – David Carlisle Mar 7 at 13:59
1

The argument of a macro defined with \def does not allow \par tokens. Neither it allows blank lines, because they're transformed to \par during the phase in which TeX processes text input into tokens. Note that redefining \par is useless in this respect, because it's precisely the token \par that's disallowed, independently of its meaning.

Solution: make your macro \long.

\long\def\mymacro#1{#1}

Better solution:

\newcommand{\mymacro}[1]{#1}

because \newcommand uses \long\def internally. The variant \newcommand* instead uses \def without the prefix.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.