基于逐字的可选参数的环境会导致奇怪的错误

基于逐字的可选参数的环境会导致奇怪的错误

我想定义一个使用逐字环境的可选参数的新环境。我的代码如下:

\documentclass{article}
\usepackage{verbatim}
\begin{document}

\newenvironment{smurf}[1][]{
  \verbatim
}{
  \endverbatim
}

This is okay:
\begin{smurf}
x x x x x x
\end{smurf}
bla bla bla

This is okay, too:
\begin{smurf}[2]
x x x x x x
\end{smurf}
bla bla bla

This causes a strange error:
\begin{smurf}{2}
x x x x x x
\end{smurf}
bla bla bla

\end{document}

第三个例子导致了一个奇怪的错误。TeX 说:

Runaway argument?
{2}^^Mx x x x x x^^M\end{smurf}^^Mbla bla bla^^M^^M\end{document}^^M^^M
! File ended while scanning use of \verbatim@start.

我不明白为什么这是个问题。我该如何修复它?

答案1

您的smurf环境(不改变类别代码制度)会寻找可选参数/查找是否存在,然后在“获取”可选参数(如果存在)后调用。[12\verbatim

\verbatim依次切换到 verbatim-category-code-régime 并依赖于在 verbatim-category-code-régime 下获取/被标记化的以下标记。

{但是,在第三个例子中,的开头花括号{2}并未根据 verbatim-category-code-régime 进行标记,因为在查找可选参数时,在\verbatim被调用之前,它已经在正常/未改变的 category-code-régime 下进行了标记。

因此,在执行时,\verbatim您已经有一个{类别代码 1(开始组)的字符标记,但由于\verbatim切换到逐字类别代码制度,随后的闭合花括号不会被标记为类别代码 2(结束组)字符标记,但会被标记为类别代码 12(其他)字符标记。因此,当\verbatim@start收集其参数时,它会找到{类别代码 1(开始组)的字符标记,但找不到}类别代码 2(结束组)的匹配字符标记。这会导致错误Runaway argument?

由于正常类别代码制度下的类别代码[与逐字类别代码制度下的相同,因此您可以实现一种机制,在切换到逐字类别代码制度(通过\let\do\@makeother\dospecials\@vobeyspaces\obeylines)后,通过\kernel@ifnextchar“查找”是否存在[指示可选参数的存在。

\begin{smurf}请注意,使用此方法时,您不能再通过使用换行符和空格的组合 来分离可选参数。

即,
使用

\begin{smurf}[optional]

该短语[optional]不被视为逐字列表的一部分,而是被视为可选参数。

\begin{smurf} [optional]

该短语⟨space⟩[optional]不被视为可选参数,而是被视为逐字列表的一部分。

\begin{smurf} 
[optional]

该短语[optional]不被视为可选参数,而是被视为逐字列表的一部分。

\documentclass{article}
\usepackage{verbatim}

\newcommand\FetchoptionalArgAndCallVerbatim[1][]{%
  The optional argument---in parentheses---is: (#1)%
  \verbatim
}%

\makeatletter
\newenvironment{smurf}{%
  \begingroup
  \let\do\@makeother
  \dospecials
  \@vobeyspaces
  \obeylines
  \kernel@ifnextchar[%
                    {\endgroup\FetchoptionalArgAndCallVerbatim}%
                    {\endgroup\FetchoptionalArgAndCallVerbatim[]}%
}{%
  \endverbatim
}
\makeatother

\begin{document}

This is okay:
\begin{smurf}
x x x x x x
\end{smurf}
bla bla bla

This is okay, too:
\begin{smurf}[2]
x x x x x x
\end{smurf}
bla bla bla

This is okay, also:
\begin{smurf}[{\LaTeX[]}]
x x x x x x
\end{smurf}
bla bla bla

This no longer causes a strange error:
\begin{smurf}{2}
x x x x x x
\end{smurf}
bla bla bla

\end{document}

以下示例允许您\begin{smurf}使用空格将可选参数与分隔开。
但是,未实现通过换行符进行分隔,因为这将消除逐字列表的第一行以打开的方括号开头的可能性[/这将消除逐字列表的第一行以嵌套在方括号中的内容开头的可能性。[⟨something⟩]

即,
使用

\begin{smurf}[optional]

该短语[optional]不被视为逐字列表的一部分,而是被视为可选参数。

\begin{smurf} [optional]

该短语⟨space⟩[optional]不被视为逐字列表的一部分,而是[optional]被视为可选参数。

\begin{smurf} 
[optional]

该短语[optional]不被视为可选参数,而是被视为逐字列表的一部分。

\begin{smurf}⟨space⟩
[optional]

该短语[optional]不被视为可选参数,而是被视为逐字列表的一部分。

\begin{smurf}
⟨space⟩[optional]

该短语⟨space⟩[optional]不被视为可选参数,而是被视为逐字列表的一部分。

\documentclass{article}
\usepackage{verbatim}

\newcommand\FetchoptionalArgAndCallVerbatim[1][]{%
  The optional argument---in parentheses---is: (#1)%
  \verbatim
}%

\makeatletter
\newenvironment{smurf}{%
  \begingroup
  \let\do\@makeother
  \dospecials
  \@vobeyspaces
  \obeylines
  \lookaheadloop{}%
}{%
  \endverbatim
}
\begingroup
% As catcode of space will be changed, don't indent the following lines:
\@vobeyspaces%
\obeylines%
\@firstofone{% brace-level 1
\endgroup%
\newcommand\lookaheadloop[1]{% brace-level 2
\kernel@ifnextchar{ }{\gatherspace{#1}}{%  brace-level 3
\kernel@ifnextchar[%
{\endgroup\FetchoptionalArgAndCallVerbatim}%
{\endgroup\FetchoptionalArgAndCallVerbatim[]#1}%
}%  brace-level 3
}%  brace-level 2
\@ifdefinable\gatherspace{\long\def\gatherspace#1 {\lookaheadloop{#1 }}}%
}%  brace-level 1
% Now the catcode of space isn't changed any more.
\makeatother

\begin{document}

Example 1:
\begin{smurf}
x x x x x x
\end{smurf}
End of example 1.

Example 2:
\begin{smurf}[2]
x x x x x x
\end{smurf}
End of example 2.

Example 3:
\begin{smurf} [{\LaTeX[]}]
x x x x x x
\end{smurf}
End of example 3.

Example 4:
\begin{smurf}[{\LaTeX[]}]
x x x x x x
\end{smurf}
End of example 4.

Example 5:
\begin{smurf} 
[{\LaTeX[]}]
x x x x x x
\end{smurf}
End of example 5.

Example 6:
\begin{smurf} 
 [{\LaTeX[]}]
x x x x x x
\end{smurf}
End of example 6.

Example 7:
\begin{smurf}

[{\LaTeX[]}]
x x x x x x
\end{smurf}
End of example 7.

Example 8:
\begin{smurf}{2}
x x x x x x
\end{smurf}
End of example 8.

Example 9:
\begin{smurf}   {2}
x x x x x x
\end{smurf}
End of example 9.

Example 10:
\begin{smurf}   
{2}
x x x x x x
\end{smurf}
End of example 10.

\end{document}

答案2

您正在强制向前查看是否存在可选参数。

\begin{smurf}{2}

没有,[所以没有可选参数,但在查看时{已经被看到并标记为一个正常的组开始{。然后逐字内容被读取并且2}逐字读取,所以永远不会有任何与之匹配的东西{

最简单的做法是使参数非可选,否则环境的第一个标记将永远不会被逐字读取。

不过你也许可以设置{之前的 catcode:

\documentclass{article}
\usepackage{verbatim}
\begin{document}

\newenvironment{smurf}{% you need this % anyway
  \catcode`\{=12 %
  \smurfx
}{%
  \endsmurfx
}

\newenvironment{smurfx}[1][]{% you need this % anyway
  \verbatim
}{
  \endverbatim
}

This is okay:
\begin{smurf}
x x x x x x
\end{smurf}
bla bla bla

This is okay, too:
\begin{smurf}[2]
x x x x x x
\end{smurf}
bla bla bla

This causes a strange error:
\begin{smurf}{2}
x x x x x x
\end{smurf}
bla bla bla

\end{document}

相关内容