![我该如何在纯 TeX 中定义一个字符来开始和结束一个环境?](https://linux22.com/image/451580/%E6%88%91%E8%AF%A5%E5%A6%82%E4%BD%95%E5%9C%A8%E7%BA%AF%20TeX%20%E4%B8%AD%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E6%9D%A5%E5%BC%80%E5%A7%8B%E5%92%8C%E7%BB%93%E6%9D%9F%E4%B8%80%E4%B8%AA%E7%8E%AF%E5%A2%83%EF%BC%9F.png)
在 中TeX
,数学模式的开始和结束使用相同的符号,即$
。假设我想开始和结束这样的环境,也许使用斜体文本,使用字符*
,如下所示。代码*this is italic*
将排版此文本为斜体。我尝试了以下解决方案,但是没有效果。
\catcode`*\active
\def*#1{\bgroup\it#1\egroup}
*this is italic*
\bye
我也尝试过
\catcode`*\active
\newif\ifitalic
\def*#1{\ifitalic\egroup\italicfalse\else\italictrue\bgroup\it#1\fi}
*this is italic*
\bye
但他们都抛出了错误
Runaway argument?
! Paragraph ended before * was complete.
我究竟做错了什么?
答案1
您的问题是,由于您说,即 是未分隔的参数,因此您的宏*
仅读取单个后续标记(忽略空格后)。 的第一次使用会到达,而第二次使用(在短语末尾)会到达(如果后面有空行),结果是“段落在 \par 之前结束”,因为未定义为。如果没有空行(您的示例中正是如此),则扫描到,结果是“禁止的控制序列”,因为定义为纯 TeX 中的。\def*#1{...}
#1
*
t
#1
\par
#1
*
\long
\bye
\bye
#1
\bye
\outer
您可以定义\startit
不带参数的“切换宏”,如下所示:
\def\startit{\bgroup \let\startit=\egroup \it}
\catcode`*=13
\def*{\startit}
Test *this is italic*
\bye
第一次使用*
启动组,选择斜体字体并重新定义\startit
为\egroup
。第二次使用*
(在短语末尾)\startit
以 含义运行\egroup
并关闭组(现在未选择斜体字体)。此外,\startit
之后 恢复其原始含义\egroup
,因此下次可能*
再次打开斜体文本。
*
Egreg 展示了如何将斜体含义和**
粗体含义结合起来的示例。此外,**
斜体不执行任何操作,而*
粗体选择粗体斜体。通过以下七行代码可以实现相同的行为:
\font\bi=cmbxti10
\catcode`\*=13
\def*{\activeast}
\def\activeast{\futurelet\next\aastA}
\def\aastA{\ifx*\next \expandafter \startbf \else \startit \fi}
\def\startit{\bgroup \def\startbf*{}\let\startit=\egroup \it}
\def\startbf*{\bgroup \let\it=\bi \def\startbf*{\egroup}\bf}
Hello, *th**i**s* is a **test, *to see* whether** it works.
\bye
请注意,该代码比 egreg 的 34 行代码紧凑得多。
答案2
以下是 Bruno Le Floch 代码的“解释”https://tex.stackexchange.com/a/15374/4427
\font\bfit=cmbxti10
\catcode`@=11
% boilerplate
\long\def\@gobble#1{}
\long\def\@firstofone#1{#1}
\def\star@out{%
\star@ifnext{\bgroup\bf\let\star@current\star@inbf\@gobble}%
{\bgroup\it\let\star@current\star@init}%
}
\def\star@inbf{%
\star@ifnext{\egroup\@gobble}{\bgroup\bfit\let\star@current\star@initbf}%
}
\def\star@init{%
\star@ifnext{\bgroup\bf\let\star@current\star@initbf}{\egroup}%
}
\def\star@initbf{\star@ifnext{\egroup\@gobble}{\egroup}}
\let\star@current\star@out
\def\star@ifnext#1#2{%
\def \reserved@a {#1}%
\def \reserved@b {#2}%
\futurelet \@let@token \star@ifnext@aux
}
\begingroup
\catcode`\*=13
\@firstofone{\endgroup
\def*{\star@current}
\def\star@ifnext@aux{%
\ifx \@let@token *\let \reserved@c \reserved@a
\else \let \reserved@c \reserved@b
\fi
\reserved@c
}
}
\catcode`@=12
\catcode`\*=\active
Hello, *th**i**s* is a **test, *to see* whether** it works.
\bye
答案3
使用字符*
作为参数分隔符。
\catcode`\*=\active
\def*#1*{\bgroup\it#1\egroup}
*this is italic*, but this isn't
\bye