(前回から大分時間が過ぎてしまったが、「暗黙的な波括弧は波括弧か波括弧でないか (3)」の続き。)
問題になっているのは、「明示的な波括弧」(つまりカテゴリコード 1/2 の {
や }
)をマクロの本体部に含める場合には常に括弧の対応が取れている必要があるという制限である。この制限は、「マクロ定義の本体部は {
で開始し、それと対応する }
で終了する」という文法規則に起因する。このため、例えば本体部に }
だけを含むようなマクロ(今の問題の \enddefqverb
)は定義できない。
ところが、この制限を回避するためのギミックとして、「\ifnum
トリック」というものがある。これは、どうしても単独の {
や }
をマクロ本体に入れたいという場合に、
{
の代わりに{\ifnum`}=\z@\fi
を書く}
の代わりに\ifnum`{=\z@\fi}
を書く
というものである。ここで余分に入れた \ifnum...\fi
のコードは実行しても何も起こらないし、展開限定文脈であっても展開されて消える。((`}
は〈}
〉の文字コードである整数 125 を表す。\z@
は定数ゼロなので、\ifnum`}=\z@\fi
は 125 = 0 という比較をすることになり、結果偽となり、結局何も残らない。さらに、TeX の条件トークンは展開可能である。))しかし、`
に続く {
や }
は確かに明示的な波括弧のトークンなので、トークンの「意味」が解釈されない文脈においては、各々のトークン列は波括弧について対応が取れているように見える訳である。
というわけで、この「\ifnum
トリック」を今回の問題に適用してみる。
\def\defqverb#1{ 〔カテゴリコードの変更の処理〕\xdef#1{\ifnum`}=\z@\fi} \def\enddefqverb{\ifnum`{}}
これで実際に defqverb 環境が期待通りの動作をするかを確かめてみよう。ここで、\begin
と \end
が実質的に次のようなトークン列と等価である(...
の部分は補助的な操作((例えば \@currenvir
の設定や検査等。))が入る)ことに注意する。((ここで \XXX
と \endXXX
がそれぞれ最後と最初にあることが肝要である。))
\begin{XXX}
→... \begingroup ... \XXX
\end{XXX}
→\endXXX ... \endgroup ...
それでは、前回の記事で示した「こうなってほしい例」のコードの実行を追ってみよう。(なお、水色の部分はまだトークン化されていないテキストを表す。)
\begin {defqverb}\Sample\Test{#%$%#}\end{defqverb}
〔\begin
前処理、局所化グループに入る〕
→\defqverb \Sample \Test{#%$%#}\end{defqverb}
〔カテゴリコード変更の処理((もちろん、カテゴリコード変更の影響を受けるのはまだトークン化されてない入力テキスト(\Test
以降)だけである。))〕
→\xdef \Sample {\ifnum `}=\z@ \fi \Test{#%$%#}\end{defqverb}
〔\Sample
の定義本体を開始、\xdef
だから展開は有効〕
→\ifnum `}=\z@ \fi \Test{#%$%#}\end{defqverb}
〔\ifnum
が展開されて下線部が消える〕
→\Test {#%$%#}\end{defqverb}
〔\Test
を展開。#%$%#
はカテゴリコード 12 のトークンの列になる((\ { }
のカテゴリコードはそのままなので、\Test
は制御綴となり、続く{...}
がマクロ引数と見做される。次のステップの\end{defqverb}
でも同じ。))〕
→Test[#%$%#]\end {defqverb}
〔\end
を展開〕
→Test[#%$%#]\enddefqverb ... \endgroup ...
〔\endqverb
を展開〕
→Test[#%$%#]\ifnum `{=\z@ \fi } ... \endgroup ...
〔\ifnum
が展開されて下線部が消える〕
→Test[#%$%#]} ... \endgroup ...
〔カテゴリコード 2 の単独の}
を検知したので\xdef
文を終結、マクロ本体は「Test[#%$%#]
」である〕
→... \endgroup ...
〔\end
の後処理を実行、グループを脱出して処理終了〕従って、「環境の内容にあるものを本体部とする」という処理がこの方針で実現できることが解った。あとは、「カテゴリコードを変える」処理や他の細部を実装すればよい。完成したものを以下に示す。((詳細仕様として決めるべき事項として、「改行の扱いをどうするか」ということがある。ここでは改行を
\par
に変換している。内容の先頭と末尾にある改行も\par
になる。))test-defqverb.tex をコンパイルすると、
\show\Sample
の結果が以下のように表示される。> \Sample=macro: ->\par TEST[#%$%#]\par OK!\par .