Как написать макрос, который принимает аргументы, содержащие абзацы?

Как написать макрос, который принимает аргументы, содержащие абзацы?

Я пытаюсь написать макрос, который принимает аргументы, содержащие абзацы. Если вы напишете обычный макрос, и один из его аргументов будет содержать абзац, он сломается:

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

\title{test}

\begin{document}

\maketitle

\section{Introduction}

\def\mymacro#1{#1}

\mymacro{This

contains a paragraph}

\end{document}

Это приводит к ошибке:

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

Поэтому я попытался переопределить \parмакрос для расширения аргументов, но теперь он не прекращает компиляцию:

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

решение1

Во времена написания TeX обработка одной страницы документа занимала несколько минут, а подсветка синтаксиса еще не существовала, поэтому было бы неплохо иметь какой-то механизм для обнаружения того, что вы забыли a }. \defПо умолчанию a не допускает токен, \parесли вы явно не укажете, что это a \long\def:

\def\mymacro#1{#1}

LaTeX, с другой стороны, использует это по умолчанию, поэтому, если вы используете правильные команды LaTeX ( \defне следует использовать в документах LaTeX), \newcommandсоздает \long\defпо умолчанию. Если вам нужно «короткое», \defто вы используете \newcommand*.

xparseвозвращает короткий аргумент по умолчанию, но позволяет определить \longмакрос с помощью +модификатора аргумента:

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

Ваша вторая попытка умна и могла бы сработать, если бы не два обстоятельства.

Первое, что вы используете \gdef\oldpar{\par}, а затем \gdef\par{\oldpar}. Как только вырасширять \parвы получаете \oldparкоторый при расширении дает \parкоторый при расширении дает который при расширении дает \oldparкоторый при расширении дает \parкоторый при расширении дает \oldpar... Работает вечно :/

В этом случае вам необходимо использовать \let(или \global\letиметь глобальный эффект): \let\oldpar\par. Это создает точную копию \parnamed \oldpar, которая не зависит от того, что есть \par.

Во-вторых, проверка аргументов на выход из-под контроля реализована на более низком уровне, независимо от определения \par, поэтому это приведет к той же ошибке:

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

bar}

потому что когда TeX видит два \endlinecharтокена (что по умолчанию является пробелом), TeX вставляет неявный \parтокен, что вызывает Runaway argumentошибку. Зная это, тогда:

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

bar}

Ошибка не возникнет, но новая строка больше не будет пробелом.

решение2

Аргумент макроса, определенного с помощью, \defне допускает \parтокены. Он также не допускает пустые строки, поскольку они преобразуются в \parво время фазы, в которой TeX обрабатывает текстовый ввод в токены. Обратите внимание, что переопределение \parбесполезно в этом отношении, поскольку этоименно тактокен \par, который запрещен, независимо от его значения.

Решение: создайте свой макрос \long.

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

Лучшее решение:

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

потому что \newcommandиспользует \long\defвнутренне. Вариант \newcommand*вместо этого использует \defбез префикса.

Связанный контент