すべての TeX 系エンジンの根っこにあるのが、クヌースが書いた オリジナルの TeX と、その上位互換である e-TeX です。このページでは、「TeX というプログラム」とは何か、plain TeX とは何か、なぜ今は誰も直接書かないのに今でも重要なのか、そして 1990 年代に追加され今では標準基盤となった e-TeX 拡張までを見ていきます。
「TeX というプログラム」とは
混乱しやすいのですが、TeX という語は二つのものを指します。ひとつは ドナルド・クヌース が書いた組版プログラムそのもの。もうひとつは、そのプログラムが解釈する命令の体系です。プログラム本体は、文字や箱を行と段落に詰め、改行位置を最適化し、結果を出力ファイルに書き出す——それだけを行う、徹底して低水準なエンジンです。
クヌースは自著『The Art of Computer Programming』の組版品質に満足できず、1978 年に最初の TeX を作り始めました。初版は手探りの試作で、1982 年に全面的に書き直され、これが TeX82 と呼ばれます。今日「TeX」と言えば事実上この TeX82 系列を指します。クヌース自身が定めた命令の最小セットを プリミティブ(基本命令) と呼び、\def・\hbox・\vbox などがそれにあたります。
プリミティブだけで文書を書くのは現実的ではないので、ふつうはその上に マクロで命令を積み上げたフォーマット を使います。plain.tex を読み込んだものが plain TeX、はるかに大きなマクロ群を読み込んだものが LaTeX です。フォーマットは起動時に .fmt ファイルとして事前展開・保存され、毎回ゼロから読み直さずに済むようになっています。
plain TeX
plain TeX は、クヌースが『The TeXbook』で示した標準フォーマットです。\font の設定、基本的な数式記号、\bye のような便利マクロなど、最低限の道具立てを plain.tex にまとめたもので、TeX 本体に付属します。LaTeX が登場する前は、これが TeX を使う唯一の現実的な方法でした。
記法は LaTeX とかなり違います。インライン数式は $...$(ここは LaTeX と同じ)。水平な箱は \hbox{...}、垂直な箱は \vbox{...}、表組みは \halign で揃え、命令の定義は \def、本文幅は \hsize、そして文書の終わりは \bye です。\documentclass も \begin{document} もありません——それらはすべて LaTeX がマクロで作ったものだからです。
% plain TeX — tex hello.tex で処理 / process with: tex hello.tex
\hsize=10cm
\font\big=cmr10 at 17pt
{\big Hello, plain \TeX!}
\medskip
This paragraph is set in the default font.
Inline math works too: $E = mc^2$.
\bye上の例を tex hello.tex で処理すると DVI ファイルができます(PDF ではありません——後述)。\TeX のようなロゴ用マクロや \medskip(中くらいの縦アキ)も plain TeX が定義しています。LaTeX の \documentclass 文書と見比べると、plain TeX がいかに「素のエンジンに薄く皮を被せただけ」かがよく分かります。
文芸的プログラミングと WEB
TeX 自体は、クヌースが提唱した 文芸的プログラミング(literate programming) の実践として、WEB という独自システムで書かれています。WEB のソースは、人間向けの解説と Pascal のコードが交互に織り込まれた一本の文書で、そこから二つのツールが派生物を取り出します——tangle がコンパイル可能な Pascal を、weave が組版された解説書(『TeX: The Program』)を生成します。
当初の対象言語は Pascal でしたが、現代の配布物では web2c というツールが WEB(経由の Pascal)を C 言語 に変換してビルドします。つまり、いま手元で動いている pdfTeX や LuaTeX も、たどっていけばこの一本の文芸的ソースに行き着くわけです。
π に収束するバージョン番号
TeX のバージョン番号は独特です。バージョン 3 以降、更新のたびに末尾へ桁を一つ足していき、番号が 円周率 π に漸近 していきます。現在のバージョンは 3.141592653…。これは「もう大きな変更はなく、極めて安定している」ことの象徴です。
クヌースは、自分の死後に行う「最後の変更」でバージョンをちょうど π に固定すると宣言しています。そのとき、残っているバグはすべて永久に「仕様」になります。事実、TeX は何年も新機能が加わっておらず、報告される不具合もごくわずかな修正にとどまります。この 凍結された安定性 こそ、何十年も前の .tex ファイルが今でも同じ出力を生む理由です。
METAFONT と DVI
TeX には双子の伴侶 METAFONT(mf)があります。これはフォントを「形」ではなく「描き方のプログラム」として記述するシステムで、クヌースは TeX 用の Computer Modern 書体一式をこれで設計しました。TeX が文字をどこに置くかを決め、METAFONT がその文字の形そのものを生み出す、という役割分担です。
クヌースの TeX が直接吐き出すのは PDF ではなく DVI(DeVice Independent) ファイルです。DVI には「この位置にこの文字を置く」といった、特定の出力機器に依存しない指示だけが入っています。そこから dvips で PostScript に、dvipdfmx で PDF に変換します。今日広く使われる pdfTeX・XeTeX・LuaTeX は、この一段を省いて直接 PDF を生成できる点が大きな違いです。
なぜ今でも重要なのか
現実には、生の plain TeX を新規に書く人はほとんどいません。番号付け・相互参照・クラスファイルといった実用機能はすべて LaTeX が上に積んでおり、日常の執筆はその恩恵の上で行われるからです。それでも、ここで触れたプリミティブを知っておく価値は大きいものです。
- あらゆるフォーマットの土台。 LaTeX も ConTeXt も、結局はこれらのプリミティブの上に建っています。
- エラーの底が見える。 込み入ったエラーや低水準の調整は、
\hboxや\vboxの語彙が分かると一気に読み解けます。 - 箱とグルーの世界観。 TeX が紙面を「箱(box)」と「伸縮するアキ(glue)」で組み立てるという考え方は、LaTeX を深く使う上での共通言語です。
- 普遍性と再現性。 凍結されたエンジンゆえ、過去の文書が将来も同じ結果を生みます。
e-TeX とは
クヌースが TeX を凍結したことで、新しい機能の追加は別の手で行うしかなくなりました。そこで 1990 年代、ヨーロッパの NTS(New Typesetting System)プロジェクト から生まれたのが e-TeX です。主たる開発は Peter Breitenlohner が担いました。e-TeX は TeX を置き換えるものではなく、TeX の厳密な上位互換(strict superset) として設計されています——既存の入力はそのまま、出力も変えずに動きます。
e-TeX は二つの動作モードを持ちます。互換モード では昔ながらの TeX と完全に同じに振る舞い、* を付けて起動する 拡張モード(extended mode) で初めて追加プリミティブが有効になります。フォーマット構築時に拡張モードで作られていれば、利用者は意識する必要はありません。
いまや標準基盤
今日では e-TeX 拡張は「あるのが当たり前」の基盤です。TeX Live は 2003 年から e-TeX を既定にし、LaTeX は 2017 年以降、e-TeX プリミティブの存在を正式に前提としています。さらに重要なのは、現役のエンジンがすべて e-TeX を内蔵していることです——pdfTeX・XeTeX・LuaTeX は、いずれも e-TeX 拡張を取り込んだ上に独自機能を重ねています。
つまり、あなたが pdfLaTeX や LuaLaTeX で文書を組むとき、知らないうちに e-TeX の上で動いているのです。expl3(LaTeX3 のプログラミング層)をはじめ、現代の多くのパッケージは e-TeX プリミティブなしには成り立ちません。
e-TeX が追加したプリミティブ
e-TeX の追加機能は、マクロを書く人にこそ効いてきます。とりわけ大きいのが 整数・寸法・グルーの算術。素の TeX では数値計算に補助レジスタを使い回す必要がありましたが、e-TeX は \numexpr・\dimexpr・\glueexpr を備え、(a+b)*c/d のような式をその場で展開可能な形で評価できます。
% 拡張モードの e-TeX 系エンジンで / on any e-TeX engine in extended mode
\count0=\numexpr (3+4)*2/7 \relax % 2 が入る / yields 2
% プリミティブが「定義済みか」を安全に分岐 / branch safely on whether a name is defined
\ifdefined\foo \message{foo exists}\else \message{no foo}\fi
% 制御綴を作らずに名前の存在を確かめる / test a control sequence without creating it
\ifcsname chapter\endcsname \message{chapter is defined}\fi
% \unless で「if の否定」を直接書く / negate a conditional directly
\unless\ifnum\count0>10 \message{count0 is not greater than 10}\fiもうひとつの柱が 条件判定とトークン操作。\ifdefined は制御綴が定義済みかを、\ifcsname...\endcsname は名前から組み立てた制御綴の存在を——いずれも 副作用なしに——調べられます(素の TeX で \ifx を使う古い手口は、未定義の綴をうっかり \relax 化してしまう罠がありました)。\unless は任意の条件を反転させ、\detokenize はトークン列をその文字列表現(カテゴリーコード 12 の文字列)に変えます。
展開制御も強化されました。\unexpanded は中身を展開せずにそのまま残し、\protected は定義したマクロを「\edit/\write などの展開文脈でも勝手に展開されないマクロ」にします。これは LaTeX の \protect を低水準で正しく実現する鍵です。
| プリミティブ | 役割 |
|---|---|
\numexpr / \dimexpr / \glueexpr | 整数・寸法・グルーの式をその場で評価(展開可能) |
\ifdefined / \ifcsname | 制御綴が定義済みか/存在するかを副作用なしに判定 |
\unless | 直後の条件判定の真偽を反転させる |
\protected | 展開文脈で勝手に展開されないマクロを定義する |
\detokenize / \unexpanded | トークン列を文字列化/展開せずそのまま保持 |
\scantokens / \readline | 文字列を改めてトークンとして読み直す/入力行を生に読む |
\middle | \left … \right の途中に伸縮する区切りを置く |
\currentgrouplevel / \interactionmode | 現在のグループの深さ/対話モードを参照・設定する |
このほか、数式で \left( … \middle| … \right) のように 中間の伸縮区切り を置ける \middle、文字列を改めてトークンとして読み直す \scantokens、入力行を生のまま読む \readline、現在のグループの深さを返す \currentgrouplevel、対話モードを参照・変更する \interactionmode などがあります。さらに e-TeX は 双方向組版(右横書き)のフック を導入し、これは後の XeTeX・LuaTeX や日本語処理にもつながりました。
地味ですが効く拡張が レジスタの大幅増加 です。素の TeX は \count・\dimen・\skip・\toks などをそれぞれ 256 個しか持ちませんでしたが、e-TeX はこれを 32768 個 に拡張しました(疎な配列として確保されます)。大きなクラスやパッケージを多数読み込んでもレジスタが枯渇しにくくなり、現代の重厚な LaTeX 環境を支える縁の下の力持ちになっています。