Como você escreve uma macro que aceita argumentos contendo parágrafos?

Como você escreve uma macro que aceita argumentos contendo parágrafos?

Estou tentando escrever uma macro que receba argumentos contendo parágrafos. Se você escrever uma macro normal e um de seus argumentos contiver um parágrafo, ela irá quebrar:

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

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\def\mymacro#1{#1}

\mymacro{This

contains a paragraph}

\end{document}

Isso produz um erro:

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

Então tentei redefinir \parpara expandir os argumentos da macro, mas agora não para de compilar:

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

Responder1

Na época em que o TeX foi escrito, uma página de um documento demorava vários minutos para ser processada, e o realce de sintaxe não era uma coisa, então era bom ter algum mecanismo para detectar se você esqueceu um arquivo }. A \def, por padrão, não permite um \partoken, a menos que você diga explicitamente que é um \long\def:

\def\mymacro#1{#1}

O LaTeX, por outro lado, usa isso por padrão, então se você usar comandos LaTeX adequados ( \defnão devem ser usados ​​em documentos LaTeX), \newcommandcria um \long\defpor padrão. Se você quiser um “short”, \defentão você usa \newcommand*.

xparseretorna o argumento curto padrão, mas permite definir uma \longmacro usando o +modificador de argumento:

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

Sua segunda tentativa é inteligente e poderia ter funcionado, exceto por duas coisas.

Primeiro é que você está usando \gdef\oldpar{\par}e depois \gdef\par{\oldpar}. Uma vez que vocêexpandir \parvocê obtém \oldparo que, quando expandido, produz \paro que, quando expandido, produz \oldparo que, quando expandido, produz \paro que, quando expandido, produz \oldpar... Correndo para sempre:/

Você precisa usar \let(ou \global\letpara ter efeito global) neste caso: \let\oldpar\par. Isso cria uma cópia exata \pardenamed \oldparque não depende de what is \par.

Segundo, a verificação do argumento descontrolado é implementada em um nível inferior, independente da definição de \par, portanto, isso falharia com o mesmo erro:

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

bar}

porque quando o TeX vê dois \endlinechartokens (que é um espaço por padrão), o TeX insere um \partoken implícito, o que gera o Runaway argumenterro. Sabendo disso, então:

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

bar}

não gerará um erro, mas uma nova linha não será mais um espaço.

Responder2

O argumento de uma macro definida com \defnão permite \partokens. Também não permite linhas em branco, pois elas são transformadas \pardurante a fase em que o TeX processa a entrada de texto em tokens. Observe que a redefinição \paré inútil a esse respeito, porque éprecisamenteo token \parnão permitido, independentemente do seu significado.

Solução: faça sua macro \long.

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

Melhor solução:

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

porque \newcommandusa \long\definternamente. A variante \newcommand*usa \defsem o prefixo.

informação relacionada