我正在尝试编写一个接受包含段落的参数的宏。如果您编写一个普通宏并且其参数之一包含一个段落,它将中断:
\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 的时候,处理一页文档需要几分钟,而且语法高亮还不存在,所以最好有某种机制来检测你是否忘记了}
。\def
默认情况下, 不允许使用\par
标记,除非你明确说明它是\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
的精确副本,而不依赖于。\par
\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
token。它也不允许使用空行,因为\par
在 TeX 将文本输入处理为 token 的阶段,空行会被转换为 token。请注意,\par
在这方面重新定义是无用的,因为它恰恰不允许的标记\par
,不管其含义如何。
解决方案:制作宏\long
。
\long\def\mymacro#1{#1}
更好的解决方案:
\newcommand{\mymacro}[1]{#1}
因为内部\newcommand
使用。变体改为使用不带前缀的。\long\def
\newcommand*
\def