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

2016-11-24 (updated: 2018-08-26)  #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の文字列」がコントロール・ワードとしてまとめられます. ↩︎