マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

本当は怖いトークンの定義の話

……いや、別に全然怖くなんかないですよ!

いきなり問題。次の TeX のソースを解析すると何個のトークンからなると見做されるか。ただし、カテゴリコードの設定は LaTeX の通常通りであると仮定する。

\someproc A  {B C\relax  } {`\\}\nil %?

……いえ、別に「ひっかけ」はないので、素直に答えて大丈夫です…。そう、「15」ですね。やっぱり何も怖くなかった。めでたしめでたし。

*   *   *

しかし、恐らくは答えが「5」とか「7」とかの全然違う数になった人がいるだろう。そういう人はトークンの定義を誤解している可能性が高い。

粗く言うと、1 つのトークンとは、1 つの制御綴である、または字句解析で無視されない部分の文字(空白文字を含む)の 1 つのことである。今の問題の場合、「複数の空白文字は 1 つと見做される」「制御綴の直後の空白文字は無視される」「コメントは無視される」という規則に従うと、無視されない制御綴と文字は以下のようになり、これらの各々がトークンであるということになる。

\someproc」「A」「空白」「{」「B」「空白」「C」「\relax」「}」「空白」「{」「`」「\\」「}」「\nil

よくある誤解は、トークン化のルールを「区切り無しマクロの引数対応」と混同することであろう。例えば、上の例でもし \someproc

\def\someproc#1#2#3#4{...}

のように定義されたマクロだとすると、引数の対応は以下のようになる。

#1 -> A
#2 -> B C\relax
#3 -> `\\
#4 -> \nil

つまり、空白トークンは読み飛ばされ、グループ { ... } は単一の引数と扱われる。しかし、これは「マクロの引数のルール」であり、トークン化とは関係がない。つまり、

グループはトークンではない

のである。もしこれを誤解していたなら、本当に恐ろしいことになる。なぜなら、トークンの定義の理解は、他の様々な TeX の機能の理解のための前提知識となっているからである。いくつか例を挙げてみる。

\ifx{foo}{#1}...\fi

{foo} とか foo とかはトークンではない。{ とか f とかがトークンである。従って、この if 文は {f を比較するので、#1 の内容に関わらず常に偽となる。

\expandafter\xx@process\expandafter{\xx@args\xx@nil}\xx@postproc

{\xx@args\xx@nil}トークンではない。{トークンである。従って、このトークン列を 1 回展開した時に展開されるのは \xx@postproc ではなく \xx@args である。

\futurelet\xx@token\xx@inspect{x}

\xx@token{ に let される。x ではない。

無論大抵の場合はトークンの理解が誤っているとプログラムが思うように動かないので、デバッグの過程で誤解に気づくだろう。だから、これまである程度の量の TeX 言語のコードを書いている人が、この記事を見た後で、青ざめた顔で自分の今まで書いたコードを片端から読み返している、なんて本当に恐ろしいことは幸いにも起こらない……はず……である。

ちなみに、LaTeX ではトークンの概念は表立って必要となることがほとんどなく、それゆえ LaTeX の参考書では「トークン」の言葉を使わないことが多い。一方で、命令(= TeX 言語のマクロ)の引数の規則は大概は説明がある。したがって、その状態で TeX 言語の学習を始めた時に混同が起きやすいのだと思う。