TeX by Topicゼミ (1) TeX処理系の構造

2016-11-24 (updated: 2025-12-09) #TeX

現在、東大TeX愛好会では隔週でTeX by Topicの輪読ゼミを行っています。当ゼミは毎回5人ほどの人数で、原則としては1回に2章というペースでTeX by Topicを読み進めています。既に3回ゼミが行われている状況ですが、今回から始まる当ブログの連載では、参加者レポートという形でこのゼミを通して得られた知見の一部を共有していこうと思います。

といっても、ゼミ1回ごとに記事を書くのは大変で長続きしなそうなので、記事とゼミの回数は敢えて1対1対応させないことにします。また、ゼミで扱った内容すべてを文書化することは困難な上、そもそもTeX by Topicに書かれている内容を丸ごと書き写すようなことになってしまうので、本連載では私が特に関心をもった話題に絞ってまとめていくことにします。

第1回の本稿では、まずTeX by Topicについての紹介とその読み方について簡単な注意を述べ、続いて第1章に書かれているTeX処理系に関する基本的な事項を見ていきます。

TeX by Topicについて

TeX by Topic—A TeXnician’s Reference(Victor Eijkhout著)は、そのサブタイトルにもあるようにTeXnicianを目指す人向けにTeX(LaTeXではない)についての細かな知識をテーマごとに分けて詳述したもので、TeX言語をきちんと扱えるようになるためのテキストとして定評があります。

書籍はもちろん売り物なのですが、原著PDFはGFDLの下で無料配布されており、著者のサイトから自由にダウンロードすることができます。また、TeX Liveがインストールされた環境であれば、そのドキュメントディレクトリに最初から含まれているので

$ texdoc texbytopic

などとすると簡単に読むことができるはずです。以上は原著(英語)の話でしたが、TeX by Topicには『TeX by Topic—TeXをより深く知るための39章』(富樫秀昭訳)というタイトルで日本語訳も存在します(ただし絶版のようです)。

TeX by Topicを読み進める上での注意

TeX by Topicでは随所に説明のためのサンプルコードが登場します。まじめにこれを読み進めるのであれば、これらのコードは(全部でないにしても)実際に入力して実行結果を確かめてみる必要があるでしょう。場合によってはそのコードを改造して新たな実験をする必要もあるかと思います。

そうした場合の注意なのですが、TeX by TopicのサンプルコードをLaTeX上で動かすことはおすすめできません。日本のTeXユーザのほとんどはLaTeXを標準の環境として利用していることと思いますが、それに甘んじてLaTeX上で実験をしているとしばしば痛い目に遭います。

どういうことかというと、LaTeXは様々な一般的な名称の命令を定義しますし、ひどい場合にはプリミティブを上書きするので(\endが代表的)サンプルコードが思うように動かない場合が多発します。実際、第1章に

\def\a#1\stop{ ... }
\a bc{d\stop}e\stop

というようなコードが登場するのですが、LaTeXが\stopというマクロを定義しているため、意図通りに動きません。ただでさえ難解と言われるTeX言語を扱っているのに、LaTeXというある種の“魔物”の影響まで考慮しなければならない状況下で実験を行うのは、まったく賢いやり方とは言えないでしょう。

したがって、TeX by Topic中のサンプルコードを実行する際は多少面倒でも plain TeX上で動かすことを強く推奨します。

TeX処理系の階層

つい前置きのようなモノが長くなってしまいましたが、ここからは実際にTeX by Topicで扱われている内容について考えていきます。最初の章では、TeX処理系の基本的な構造について述べられています。

原始的なTeXの処理系は(対話モードのことはさて措くとして)*.texというテキストファイルから*.dviファイルを生成するものです。そして、その過程は以下の4つの階層に分けて考えることができます。

  1. 入力プロセッサ(input processor)
  2. 展開プロセッサ(expansion processor)
  3. 実行プロセッサ(execution processor)
  4. ビジュアルプロセッサ(visual processor)

これらのプロセッサはKnuthによってそれぞれTeXの目・口・胃・腸に喩えられていますが、以下ではそれぞれの役割を簡単に見ていきます。

入力プロセッサ

これはTeX処理系の最初の段階です。すなわち、TeXソースそのもの(文字コードの羅列)を入力として受け取って、これをトークンのリストにするところまでがこのプロセッサの役割です。具体的には以下の2つの作業を行います。

  1. ^^+のように入力された文字を処理する
  2. 入力文字列をトークン化する

これだけです。1.の処理についてもう少し正確に述べると、2連続するカテゴリーコード7の(同一の)文字に続く文字の内部コード(実質ASCIIコード)に'100を増減するということです。この機能は初期のコンピュータでキーボードの文字種が少なく、入力できない文字がある場合に備えて用意されたもので、現在普通の (La)TeXユーザが使用することは稀だと思います1。しかし、この処理がトークン化よりも先んじて行われているということはとても重要です。

続く2.の処理についてはここでは詳しく述べません。もし、TeXのトークンについてご存知ないということであれば、一度TeX WikiのTeX入門/マクロの作成というページを読んでみるといいでしょう。

展開プロセッサ

このプロセッサは入力プロセッサからトークン列を受け取って、文字通りマクロや一部プリミティブの展開を行います。展開というのは要するに文字列の置換操作です。例えば\hogeというマクロが

\def\hoge{fuga}

のように定義されていた場合、TeXソース中で\hogeが登場したら、それはfugaという文字列に置換されます。これが展開です。すべてのTeXマクロはこの展開だけで成り立っています。また、一部のプリミティブも展開可能です。例えばpiyo.texの処理中に現れた\jobnameプリミティブはpiyoという文字列に展開されます。

展開プロセッサは、マクロと展開可能なプリミティブをすべて展開し、その結果を実行プロセッサに渡します。

実行プロセッサ

実行プロセッサは展開不能なプリミティブの実行を行います。これには例えば\show命令の実行などが含まれるでしょう。そして、実行プロセッサは最終的にはビジュアルプロセッサに渡すためのリスト(水平リスト・垂直リスト・数式リスト)を構築します。

ビジュアルプロセッサ

これがTeX処理系の最後にして最も重要な段階です。すなわち実行プロセッサから受け取ったリストを元に段落分割・行分割・ページ分割・数式組版などを行い*.dviファイルを生成します。しかし、この段階についてはユーザが介入することはほとんどできません。少なくとも、一般のTeXユーザにはほぼ手を出せない領域です2

本当のTeXはもっとアレ

ここまでTeXの処理系は4つの階層にできるとして、それぞれの役目について述べてきたところ、ここにきて言うのもなんですが、上記の説明は全部ウソです。実際のTeX処理系は、あのように4つに分割して説明できるほど単純なものではありません。

そのことを示す例をいくつか挙げてみましょう。

まず前節の話では「展開プロセッサがマクロを展開し切ってから、実行プロセッサが実行処理を行う」ということになっていましたが、マクロの定義自体(例えば\def命令の実行)は、上記の4階層の中では当然実行プロセッサが行うと考えるべきものです。したがって、「展開プロセッサがマクロを展開し切ってから、実行プロセッサが実行処理を行う」という単純な定式化は破綻します。実際には実行プロセッサの結果が展開プロセッサの動作に影響を与える、換言すれば展開プロセッサと実行プロセッサの処理は同時進行的に(もしくは協働しながら交互に)行われているということです。

もっと根本的な部分では、そもそも「入力プロセッサがトークン化を行って、展開プロセッサや実行プロセッサにトークン列を渡す」という点がまず正しくありません。トークン化を行うにはカテゴリーコードの情報が不可欠ですが3、このカテゴリーコードはTeXの命令によって動的に変更することができます。つまり、入力プロセッサの処理にも展開プロセッサや実行プロセッサの実行結果の情報が必要とされるわけです。全体として、少なくとも実行プロセッサの処理までが協働して行われない限り、TeXの処理系は字句解析さえ最後まで完了することができないという恐ろしい性質が浮かび上がります。

要するに、TeXの処理系は実際には前節で説明したような4つのプロセッサに単純に分かれているわけではありません。TeXの内部ではもっと流動的にソースの処理が行われています。TeXの処理系が入力プロセッサ・展開プロセッサ・実行プロセッサ・ビジュアルプロセッサの4つのプロセッサの階層構造から成り立っているとする説明は、話をわかりやすくするためにTeXの処理を単純化したモデルに過ぎないということです。

モデルは雑だが役に立つ

前節で「TeXの処理系が4つのプロセッサから成る」というモデル(4プロセッサモデル)は実際にはまったく正確でないということを説明しました。では、4プロセッサモデルを考えることは無意味なことなのでしょうか?

もちろん、そんなことはありません。

4プロセッサモデルは、TeXの挙動を理解する上では大変役に立つものです。例えば、次のようなTeXのソースを考えます。

\vs^^+ip 5cm

前に入力プロセッサの項で説明したように、4プロセッサモデルでは^^+kに置換する処理はトークン化よりも先んじて行われるので、上記のソースは

\vskip 5cm

と書いたのとまったく同じ効果を持つことになります。一方、最終的にkに置換されるような入力として\chardefを用いた

\chardef\a=`k
\a %=>k

も考えられますが、この方法を用いて

\vs\a ip 5cm

などとしても決して\vskip 5cmの意味にはなりません。\aの実行は実行プロセッサによって行われるわけですから、入力プロセッサによる字句解析はそれよりも前に行われて

[\vs][\a][i][p][ ][5][c][m]

というトークンに分解されます。したがって、4プロセッサモデルから\chardefを用いたソースは

\vs kip 5cm

という入力と等価に解釈されると推測できます。そして、実際のTeX処理系も上記のように振る舞います。

TeX全体を1つのブラックボックスと捉えていると少々複雑に思えるTeXの挙動も、4プロセッサモデルを用いればこのように容易に理解することが可能となります。

まとめ

TeXの処理系は字句解析・展開・実行・出力の処理を同時進行的に行う極めて複雑なシステムです。しかし、これらをそれぞれ1つのプロセッサが別々に担っているものとして捉え、TeXのもつ種々の機能がどの段階で処理されるものなのかを考えると、TeXの挙動を比較的容易に理解することができます。

次回:カテゴリーコードの研究


  1. 現在でもLaTeXパッケージなどでコンソールに表示する警告メッセージに改行を入れる際などにはよく^^Jのような表記が使われています。 ↩︎

  2. 強大な力をもつTeXnicianであっても「アウトプットルーチン」といえば大抵は恐れをなすというアレです。日本語の文献はほとんど存在していません(英語なら多少あるようです)。黄色い本・緑の本にこの点に関して解説する続編が書かれるというウワサがあったりなかったりするようですが、出版される日はくるのでしょうか……? ↩︎

  3. 例えば「カテゴリーコード0に続くカテゴリーコード11の文字列」がコントロール・ワードとしてまとめられます。 ↩︎