¿Cómo se escribe una macro que toma argumentos que contienen párrafos?

¿Cómo se escribe una macro que toma argumentos que contienen párrafos?

Estoy intentando escribir una macro que acepte argumentos que contengan párrafos. Si escribes una macro normal y uno de sus argumentos contiene un párrafo, se romperá:

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

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\def\mymacro#1{#1}

\mymacro{This

contains a paragraph}

\end{document}

Esto produce un error:

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

Entonces intenté redefinir \parpara expandir los argumentos de la macro, pero ahora no deja de compilarse:

\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}

Respuesta1

En el momento en que se escribió TeX, una página de un documento tardaba varios minutos en procesarse y el resaltado de sintaxis no existía, por lo que era bueno tener algún mecanismo para detectar si olvidabas un archivo }. A \def, de forma predeterminada, no permite un \partoken a menos que diga explícitamente que es \long\def:

\def\mymacro#1{#1}

LaTeX, por otro lado, lo usa de forma predeterminada, por lo que si usa los comandos LaTeX adecuados ( \defno deberían usarse en documentos LaTeX), \newcommandcrea un \long\defvalor predeterminado. Si quieres un "corto", \defentonces usas \newcommand*.

xparsedevuelve el argumento corto predeterminado, pero le permite definir una \longmacro usando el +modificador de argumento:

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

Su segundo intento es inteligente y podría haber funcionado excepto por dos cosas.

Primero es que estás usando \gdef\oldpar{\par}y luego \gdef\par{\oldpar}. Una vez túexpandir \parobtienes \oldparcuál, cuando se expande, produce \parcuál, cuando se expande, produce \oldparcuál, cuando se expande, produce \parcuál, cuando se expande, produce \oldpar... Corriendo para siempre :/

Es necesario utilizar \let(o \global\lettener un efecto global) en este caso: \let\oldpar\par. Esto crea una copia exacta de \parnombrado \oldparque no depende de lo que es \par.

En segundo lugar, la verificación de argumentos fuera de control se implementa en un nivel inferior, independientemente de la definición de \par, por lo que fallaría con el mismo error:

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

bar}

porque cuando TeX ve dos \endlinechartokens (que es un espacio por defecto), TeX inserta un \partoken implícito, lo que genera el Runaway argumenterror. Sabiendo eso entonces:

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

bar}

no generará un error, pero una nueva línea ya no será un espacio.

Respuesta2

El argumento de una macro definida con \defno permite \partokens. Tampoco permite líneas en blanco, porque se transforman \pardurante la fase en la que TeX procesa la entrada de texto en tokens. Tenga en cuenta que redefinir \pares inútil a este respecto, porque esprecisamenteel token \parque no está permitido, independientemente de su significado.

Solución: crea tu macro \long.

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

Mejor solución:

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

porque \newcommandse usa \long\definternamente. En cambio, la variante \newcommand*utiliza \defsin el prefijo.

información relacionada