![¿Cómo se escribe una macro que toma argumentos que contienen párrafos?](https://rvso.com/image/391694/%C2%BFC%C3%B3mo%20se%20escribe%20una%20macro%20que%20toma%20argumentos%20que%20contienen%20p%C3%A1rrafos%3F.png)
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 \par
para 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 \par
token 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 ( \def
no deberían usarse en documentos LaTeX), \newcommand
crea un \long\def
valor predeterminado. Si quieres un "corto", \def
entonces usas \newcommand*
.
xparse
devuelve el argumento corto predeterminado, pero le permite definir una \long
macro 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 \par
obtienes \oldpar
cuál, cuando se expande, produce \par
cuál, cuando se expande, produce \oldpar
cuál, cuando se expande, produce \par
cuál, cuando se expande, produce \oldpar
... Corriendo para siempre :/
Es necesario utilizar \let
(o \global\let
tener un efecto global) en este caso: \let\oldpar\par
. Esto crea una copia exacta de \par
nombrado \oldpar
que 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 \endlinechar
tokens (que es un espacio por defecto), TeX inserta un \par
token implícito, lo que genera el Runaway argument
error. 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 \def
no permite \par
tokens. Tampoco permite líneas en blanco, porque se transforman \par
durante la fase en la que TeX procesa la entrada de texto en tokens. Tenga en cuenta que redefinir \par
es inútil a este respecto, porque esprecisamenteel token \par
que 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 \newcommand
se usa \long\def
internamente. En cambio, la variante \newcommand*
utiliza \def
sin el prefijo.