とにかくWEB言語してみたい

2022-12-23 (updated: 2022-12-25)  #WEB  #TeX 

本稿はTeX & LaTeX Advent Calendar 2022の23日目の記事です。22日目はtasusuさんでした。24日目はgolden_luckyさんです。

TeXの実装言語といえばWEB言語ですが、皆さんは少しでもよいのでWEB言語でプログラムを書いたことがあるでしょうか? 多くの有名ソフトウェアはC/C++、Java、Ruby、Pythonといったメジャーな言語で書かれており(むしろ、だからこそメジャーとされるわけですが)、一般にプログラマと言われている人であればこうした言語に関しては何かしらの経験や知見があって、その気になればそのソースコードを読んで理解したり、場合によっては改造したりということができるかと思います。一方、TeXはその挙動が偏屈なのもさることながら、実装も現代的感覚からするとかなり風変わりです。実際、TeXの実装言語であるWEB言語もまたKnuthがオリジナルで考案したものであり、彼の理想である「文芸的プログラミング」を体現した一貫性のあるものではあるものの、現代のほとんどのプログラマが技術スタックとして備えているようなもので書かれているわけではありません。そのため、TeXの実装をいじろうと思っても、なかなか敷居が高いことは否めないように思います。

昨年から今年にかけてのアドベントカレンダーでは、驚くべきことにそんなWEB言語を使いこなしている凄腕のTeX開発者たちによる豪華な「WEB言語してみた」記事が登場しました。

いずれもパワフルな内容で、とても興味深い記事です。TeXマニアの皆さんはもう読まれたことと思いますが、もしまだの方がいましたら一読を強くおすすめします。こんな風にTeX処理系自体を思うがままに改造することができたら、間違いなく人生は豊かに光り輝くはずです。

一方で、WEB言語など書いたこともなければ正直どう読んでいいのかもわからない自分のような人間にとっては、これらの記事はちょっと上級者向けに感じられてしまう面もあります。どうにかWEB言語に入門したいわけですが、Google検索で「WEB言語 入門」などと検索してもそれらしき情報が見当たりません。インターネットの海からTeX言語の入門者向け情報を見つけ出すのもなかなか大変ですが、WEB言語に関してはさらに一段と入門情報の入手が難しいような感じがします。

そこで本稿は、筆者が人生で初めて「簡単なWEB言語プログラム」の作成に挑戦し、その際の理解のブレイクスルーを意識しながら、どうにかWEB言語入門を果たせるようなものを目指すことにしました。なお想定読者はなんとなくplain TeX文書が作れるレベルのTeX好きかつ何かしらのプログラミング経験がある方とします。

WEB言語とは何か

N番煎じではありますが、まずは「WEB言語とは何なのか」改めて振り返っておきましょう。

すでに前座でも触れましたが、WEB言語はKnuth自身が開発した文芸的プログラミングのための独自言語です。今どきコンピュータ関連の文脈でWebといったらWorld Wide Web (WWW)のことを指す場合が多いですが、これとは無関係です。KnuthのWEBの発表が1983年、WWWの発明が1989年なので、むしろ本稿で扱っているWEBの方が古くからあると言えそうです。WEBに関する基本的な事項に関してはKnuth自身が書いた記事で説明されています。

実際のところ、WEB開発の背景も、その設計・文法・使い方も上記の記事に解説されているので、それを読めば済む話なのですが、英語は読みたくない方もいるかもしれないので本稿で必要となる知識だけかいつまんで述べておきます。

文芸的プログラミングというのは要するに「人間が小説のように読むことのできるプログラム実装」なので、これを実現するためにWEBシステムではドキュメントとソースコードを一緒くたに1つのファイルに書き込んでいくように設計されています。KnuthのWEBでは、具体的にはドキュメント用の言語としてTeX、プログラム言語としてはPascalが採用されています。WEB言語とわざわざまったく未知の新しい言語のような言われ方をしていますが、要するにTeXとPascalの2つがわかっていれば難しいものではありません。

まず、WEB言語の場合いわゆる地の文はplain TeXによるドキュメントと解釈されます1。そこに、主として@記号から始まるWEB言語独自の記法が現れると、特殊なパートの開始が合図されることになります。下記のシンプルな例にあるもので言うと、単体の@は新しいセクションの始まりを指示して、その中身のドキュメントの記述が開始されます。また@pが現れると、それに続く部分はPascalによる実装コードと解釈されることになります。

A very simple example program.

@ Here is the top level definition.

@p
program MyProgram(output);

このようにWEB形式で記述したドキュメント+実装コードは*.webという名前のファイルに保存されます。これを下記の2種類のプログラムのいずれで処理するのかによって、プログラムの実行バイナリもしくはドキュメントを生成することができます。

  • tangle: WEBファイルからPascalのプログラムソース(*.pファイル)を生成。生成されたソースをPascalコンパイラで処理することで実行バイナリが得られる
  • weave: WEBファイルからTeXの文書ソース(*.texファイル)を生成。生成されたソースをTeXエンジンで処理することでプログラムの解説ドキュメントが得られる

WEBファイルの2通りの利用方法

ちなみに、これらのプログラムはいずれもTeX Liveに含まれているので、ローカルにTeX Liveを導入済みであれば特に何もしなくても利用することができるはずです。

WEB独自の記法(@で始まるもの)というのは、それほど種類がありません。上で言及したKnuthの論文のFigure 2a, 2b, 2cにWEB言語によるそれぞれとても小さなプログラム例が載っているのですが、そこで用いられているものだけですべてであり、10種類もない程度です。

また、TeXの実装自体がWEBで実装されており(tex.web)、これがオリジナル開発者のKnuth自身による、洗練された大きなWEBプログラムの実例となっています。あとはこれの見様見真似をすれば、それらしいWEBプログラムが書けてしまいます。

これより詳しい話は実際のWEBプログラムの例を見てからにしましょう。

WEB言語でNabeAzzしてみる

さて、では実際にWEB言語で何かコードを書いてみましょう。新しく学ぶプログラミング言語で最初に書くプログラムと言えば、いわゆるHello Worldが定番です。しかし、探してみると意外にもWEB言語でHello Worldをしているブログ記事はすでに存在していました。

ネタ被りはちょっと面白くないので、別のプログラムにしましょう。Hello Worldの次に簡単な初心者向けのお題といえばFizzBuzzなわけですが、このレベルの練習課題としてはTeX界隈ではNabeAzz問題が有名です。というわけで、今回はWEB言語でNabeAzzしてみました。

NabeAzz問題の出力は、いかにも組版システムTeXならではの見た目なのが面白いポイントなので、今回は「TeXで処理するとNabeAzzで期待される出力が得られるようなplain TeXコード」をWEBプログラムで生成することにしたいと思います。

ということで、さっそく書いてみたWEBプログラムがこちらです。

まずはこのWEBプログラムをどう使うのかを説明しましょう。

プログラムの実行方法

すでに紹介したように、WEBプログラムからPacalソースコードを得るためにはtangleと呼ばれるプログラムでnabeazz.webを処理します。

$ tangle nabeazz.web
This is TANGLE, Version 4.6 (TeX Live 2022)

Writing the output file
Done.
(No errors were found.)

こうするとPascalのソースコードがnabeazz.pというファイルに書き込まれます。中身はPascalのコードなので、読みやすくはないですが頑張れば人でも読める内容です。

$ cat nabeazz.p
{2:}program NabeAzz(output);{7:}{4:}procedure WriteAhoNumber(n:integer);
begin write('{\cmfi ');write(n);writeln('}')end;
{:4}{8:}function MultipleOfThree(n:integer):boolean;
begin if n mod 3<>0 then MultipleOfThree:=false else MultipleOfThree:=
true end;{:8}{9:}function HasDigitOfThree(n:integer):boolean;
var s:string;begin str(n,s);
if pos('3',s)<>0 then HasDigitOfThree:=true else HasDigitOfThree:=false
end;{:9}procedure AhoOrNot(n:integer);
begin if MultipleOfThree(n)then WriteAhoNumber(n)else if HasDigitOfThree
(n)then WriteAhoNumber(n)else writeln(n)end;{:7}var n:integer;
begin writeln({3:}'\font\cmfi=cmfi10 at 12pt \noindent'{:3});
{6:}for n:=1 to 400 do AhoOrNot(n);{:6}writeln({5:}'\bye'{:5});end.{:2}

これを実行可能形式にするにはPascalコンパイラが必要です。例えば私はFree Pascalコンパイラ(fpc)を利用しました2

$ fpc nabeazz.p

上記のようにコンパイルすると、実行バイナリnabeazzが得られます。もちろん実行可能です。最初に述べたように、今回のプログラムの出力はNabeAzzの解答となるplain TeXのソースコードです。したがって、単純に実行すれば長い出力が現れます。

$ ./nabeazz
\font\cmfi=cmfi10 at 12pt \noindent
1
2
{\cmfi 3}
4
5
{\cmfi 6}
7
8
{\cmfi 9}

(中略)

{\cmfi 398}
{\cmfi 399}
400
\bye

というわけなので、この出力を適当な名前のTeXファイル(ここではoutput.texとします)に書き込んで、お好きなTeXエンジンで処理をすれば目的の出力が得られます。

$ ./nabeazz > output.tex
$ pdftex output.tex

これで生成されるPDFファイル(output.pdf)の中身は次のようになっているはずです。

1〜400のNabeAzz出力

ちゃんと3の倍数と3が現れる数字でアホになっていますね。WEB言語できました。めでたし。

ドキュメントの生成方法

さて、WEBファイルのもう1つの使い方がソースコード解説ドキュメントの生成です。これにはweaveプログラムを使用します。

$ weave nabeazz.web
This is WEAVE, Version 4.5 (TeX Live 2022)

Writing the output file...
Writing the index...Done.
(No errors were found.)

こうすると、今度はnabeazz.texファイルが作成されます。中身はただのplain TeXソースです。

$ head nabeazz.tex
\input webmac
% nabeazz.web
\centerline{\bf A NabeAzz Program}
\centerline{\sl by wtsnjp}
\vskip .5cm
This is my first program to write with the \.{WEB} system. This program is a
solution of the NabeAzz problem, which is a well-known problem in Japanese
{\TeX} community as the counterpart to the FizzBuzz problem
(see \.{https://zrbabbler.hatenablog.com/entry/20110815/1313398638}).

これを適当なTeXエンジン(ワンステップでPDFを生成したいので、ここではpdfTeXを使います)で処理すると、次のような見た目の文書が得られます。

$ pdftex nabeazz.tex

NabeAzzプログラム文書

なんだかそれっぽいですね。このように、ちょっとしたプログラムでもやたらとすごそうな見た目のドキュメントにできるのがWEB言語のすごいところでした。初めてLaTeXを使ったときの感想に近いですね。中身は大したことがないのに、見た目だけ立派なものができる点が共通しているように思います。

ただ、Hello WorldやFizzBuzzやNabeAzzレベルのプログラムをWEB言語で書くのはどうにも大げさな感じがします。Java製のFizzBuzzEnterpriseEditionほどではないですが、それに似た滑稽さがありそうです。

WEBプログラムの書き方

基本的に、plain TeXとPascalが書ければ難しくはありません。

まずドキュメント部について。plain TeXを普段遣いしている人は今日日めったにいないとは思いますが、ここでのplain TeXはむしろLaTeXより簡単でしょう。特にマクロコーディングをしなければならないというわけではないので、単純に自然言語でテキストを書いていくだけです。ときどき事前に定義されている便利マクロを使用する程度です。もちろん\defを使って好きなマクロを定義することもできます。

ドキュメント部では、通常のplain TeXのマクロに加えてwebmac.texに定義されているものも利用できます。例えば\.{\macro}のようにすると、タイプライタ体で簡易\verb的な出力を得ることができます。この辺りは先述のtex.webにいくらでも実例があるので、真似して使っていけばよいだけです。

またプログラムの中身は通常のPascalコードです。Pascalは古いプログラミング言語で、もともと教育用に開発されたこともあり、仕様が簡素な手続き型言語です3。TeX言語よりもよっぽど素直で扱いやすい言語です。現代の機能がモリモリの高級言語と比べると、標準的に用意されている関数や機能は少ないですが、シンプルな構文で自前実装すればよいだけなので、あまり躓きポイントはないように思います。普通に「Pascal 入門」でウェブ検索すると、わかりやすい解説ページがたくさんヒットします。Pascalについては、わざわざ本稿で解説するまでもないでしょう。

WEB独自の記法については、すでに述べた通り10種類ぐらいしかないので覚えることは多くありません。単体の@で新しいセクションを始める、@pでパスカルのコードを書き始めるの2つを覚えるだけでも最低限使えてしまうはずです。その他に多用されるのは@<@>形式で記述されるWEBのtop-level descriptionです。top-level descriptionというのはTeXでいう単純な置換マクロ(引数を一切取らないマクロ)のようなもので、しくみはいたってシンプルです。単に名前の後ろに=を付けた行で定義を開始します:

@<some text@>=
hello world

そして、その内容はPascalコード中や、別のtop-level descriptionの定義内で利用することができます。

@p
writeln( @<some text@> )

面白いのは、こうしたtop-level descriptionはコード中、定義が現れる前でも使用できることです。そのため「ここを後で書く」という気持ちを込めて<@ essential definitions @>などと書いておくと、その中身を後で考えて定義に記入することができます。文芸的プログラミングなので、ある程度思考の順序に沿って順番に記述していけることが重要なのでしょう。

その他には@dによるマクロ定義などがありますが、いずれも単純です。説明は上述の論文(Knuth, 1983)、実例はtex.webを参照するのが簡単でしょう。

おわりに

WEB言語はコワくない☺️

おまけ:最近のpTeX系エンジンをビルドしたい

むしろここからが本編かもしれません。本稿のこれまでの部分では、雑ですがWEB言語の入門的な内容を扱いました。その結果、WEB言語はplain TeXとPascalがわかっていればそれほど難しいものではないということが明らかになりました。それでは、これで現代のpTeX系エンジン(具体的にはe-pTeXとe-upTeX)のソースコードを読み解き、改造できる技術を身に付けたと言えるのでしょうか? 残念ながら、これは必ずしもそうではありません。WEB言語の基本を理解しても、現代のpTeX系エンジンのソースコード理解にはまだまだ高い障壁がいくつも残されています。

一つは、そもそもTeXエンジンの実装が巨大であることです。オリジナルのTeX実装(本稿で何度も登場しているtex.web)にしてもそうですが、それほど小さなプログラムではありません。pTeX系のエンジンはこのオリジナルの処理系と比較してもさらに多くの機能が追加されており、より大きなプログラムとなっています。大きなプログラムの全貌を理解するのは、不可避的な困難が付き纏います。こればかりはTeXに限ったことではなく、いずれの場合もどうにもならない部分です。むしろTeXの実装に関しては、本稿でも紹介したように文芸的プログラミングのパラダイムに乗って書かれているので、後からの参入で読むには適している方だと思われます。

他にもpTeX系エンジンに実装に限った難しさというものがあります。これは、これらのエンジンの実装が本稿で紹介したような単純な単一WEBファイルによる実装になっていないことが原因です。現代のpTeXエンジン実装の作りとして、今回のtangleによるシンプルなフローとは異なる部分が少なくとも2つあります。第一に、現代のTeX LiveのWEB実装の各種TeXエンジンは、KnuthによるオリジナルのWEBシステム+Pascalでコンパイルされていません。代わりにWeb2Cという別のしくみが用いられています。第二にtex.webはいわゆるKnuthライセンスによりKnuth以外の人物によって変更を加えることができないので、pTeXの拡張部分はchange fileという特別なしくみで実現されています。これが、少なくとも傍から見ると特に厄介に思われる部分です。

このおまけパートではこれら2つの難しさにもう少しフォーカスしてみることにします。その上で、最新のTeX LiveにおけるpTeX系エンジンが、具体的にどのような手順でビルドされているのかを見ていきましょう。なおTeX Liveのソースに関する内容は、aminophenさんによる解説記事にあるような基本事項を前提知識とします。

WEBの拡張たち

KnuthによるオリジナルのWEBシステムは、すでに見てきたようにTeXとPascalをベースとした文芸的プログラミングの実現を企図したものでした。しかし、単に文芸的プログラミングという目的の達成のためであれば、ベースは必ずしもTeXとPascalである必要はありません。より今風な選択肢を考えれば、例えばMarkdownとRustでも同様のものを実現することは可能であるはずです。実際、WEBシステムは主にこの点に関してKnuth自身や他の開発者によって拡張され、さまざまな派生システムが作られてきました。特に、Pascalに関しては最近(といってもかなり範囲を広く取った「最近」ですが)は大型プログラムの開発にはC/C++が採用されることの方が多いので、こうした言語をターゲットしたものが複数存在します。具体的な派生システムとしてはCweb, CWEB, Spiderweb/noweb, Web2Cなどが挙げられます。名前が似かよったものが多いですが、すべて別物です。TeX界隈のプロダクト名は、どういうわけかいつもややこしいですね。

Cweb

CwebというのはHarold Thimblebyによって開発されたもので、オリジナルのWEBにおけるPascalをC言語に、TeXをtroffに置き換えたものです。このシステムについては原作者による論文があり、それによると「UNIX向けのWEB」として開発したという経緯のようです。

CWEB

紛らわしいことにCWEBという名前で別のシステムもあり、これはKnuth自身も参加して開発されたものです。こちらはCまたはC++とTeXの組み合わせで文芸的プログラミングをしようというもので、基本的にはctanglecweaveの2つのプログラムで構成されています。オリジナルのWEBではPascalコードを@pで開始していたのに対し、このCWEBでは@pの代わりに@cでCのコードを開始することができます4

このマニュアルはAddison-Wesleyから出版されており、なぜか我が家にも紙の本があります。

The CWEB System of Structured Documentation

本の内容としては、上に挙げたCTANのリンク先にあるのと同様の短いCWEBのマニュアルと、その後ろにctangle, cweaveの実装コード(WEBから生成されたドキュメントそのもの)がひたすら連ねられているようです。

Spiderweb/noweb

Spiderwebとその後継のnowebはいずれもNorman Ramseyの作で、一種の汎用化版WEBとでも言うべきもです。すなわち、PascalやCに限らず、任意のプログラミング言語で文芸的プログラミングを実現します。

nowebはそのウェブサイトによればAwk, C, C++, Haskell, OCaml, Perl, R, Turingなど数多くの言語で実用例があるようです。

Web2C

Web2CはここまでのWEB拡張とは少し毛色が異なります。これはオリジナルのWEBのソースファイルはそのままに、そのPascalコードをCのコードにトランスコンパイルしようとするものです。現在のTeX Liveに含まれる多数のバイナリプログラム(オリジナルのTeX、Metafont、MetaPost、一部の拡張TeXエンジン、BibTeX、DVIツール、WEBシステム、etc.)はこのツールによってC言語にコンパイルされ、世界中のTeX Live利用者によって実用されています。

またしてもややこしいことに、このPascalからCへの変換プログラムだけでなく、その変換器によって変換されている大量のプログラム群(上に挙げたようなもの)をすべてまとめてWeb2Cと呼ぶ場合があります。この意味でのWeb2Cの基本的な解説は『TeX Liveガイド』のセクション7に収録されています。筆者による日本語訳もあります。

TeX Liveのソースツリーにおいて、各種TeXエンジンのソースコードがパス位置Build/source/texk/web2c以下にあるのはそのためです5

つまり、最新のpTeX系エンジンのソースコードはKnuthによるオリジナルのWEBシステムによって生成されるPascalからコンパイルされるのではなく、ここで紹介したWeb2C実装によってC言語に変換されてからバイナリにコンパイルされています。PascalからC言語への変換は機械的に行われるので、WEBソース自体の書き方には大きな変化はないようですが、トランスコンパイル後のC言語が動作するように追加の定義ファイルを用意する必要があったり、拡張部分の一部がC言語で記述されていたりと状況はより複雑になっています。

change file

WEBによるプログラムをコンパイルするにはまずtangleコマンドを使用すると説明してきましたが、ここでこのコマンドの完全な書式を確認してみましょう。

$ tangle --help
Usage: tangle [OPTION] WEBFILE[.web] [{CHANGEFILE[.ch]|-} [OUTFILE[.p]]]

コマンドラインオプションを取ること、必須の引数としてWEBファイル(*.web)が必要であるのは普通ですね。また最後に出力先のPascalコードのファイル名(*.p)も指定できることになっています。残ったものに、CHANGEFILE[.ch]という怪しげなものがあります。

これはKnuthがWEBによって開発した各種プログラムを、特定の環境向けに若干カスタマイズするために用意した機構です。しくみとしては、diff unified形式のパッチファイルのようなもので、オリジナルのWEBファイルの一部の書き換えを指示するようなファイルです。こうしたファイルはchange fileと呼ばれ、拡張子.chで保存されます。中身は基本的には一種のWEB記法の連続です:

@x
〈WEBファイル内の変更対象の文字列〉
@y
〈変更後の文字列〉
@z

tangleの引数にこうしたchange fileを与えた場合、そこに記述された変更(パッチ)内容が元のWEBプログラムに反映され、その上でPascalコードへの変換が行われます。

ところで、オリジナルのtex.webはKnuth以外は改変してはいけないことになっているため6、現代のTeXの拡張機能はpTeX拡張も含めてすべてchange fileの形で記述されるのが慣例になっています7。また実際のpTeX系エンジンの実装においては、そのchange fileも1つや2つではなく、数十あります。pTeX拡張部分の変更履歴というのは、いわば差分の差分状態になるため、ともすると頭がこんがらがって解釈はなかなか大変です。

TeX LiveにおけるpTeX系エンジンのビルド

ここまで見てきたように最新のTeX Live向けのTeXエンジン実装は、オリジナルのWEBシステムによる単純なPascalコードへの変換・コンパイルというフローに比べると幾分複雑になっています。具体的には、大元のWEBファイルに対して大量のchange file(その中にはターゲットのC言語の関数も含まれ得る)を適用し、Web2Cの仕組みによりC言語に変換され、それが最後に各プラットフォームでCコンパイラによってバイナリに変換されます。こうした(e-)pTeXの複雑なコンパイル事情に関してはptex-manualに含まれる北川さんの文書がとても参考になります8

とはいえ上記は北川さんがe-pTeXを開発した初期の頃の話なので、現在のTeX Liveとなるとまた少し事情が変わっている部分もあります。最後に、現在のTeX Liveに含まれるpTeXソースのビルド方法を確認していきましょう。

上流ソースの所在と基本的なビルド方法

TeX Liveの心臓部(広義のWeb2C)全体のビルドを考えると大きすぎるので、ここでは日本語TeXコミュニティ(texjporg)が管理している各種TeXエンジン・DVIウェアと、最低限の依存ライブラリのみのビルドを考えます。考えますというより、便利なことにこれは既にtexjporgによってそのようなサブセットが用意されています。

すなわちこのリポジトリのsource以下が、先ほども言及したTeX LiveのソースツリーBuild/sourceの日本語TeXに関連するサブセットになっています9

このサブセット全体をビルドするのはとても簡単です。当該リポジトリをローカルに入手して、トップレベルにあるtexjpbuild.shスクリプトを実行するのみです10。実際のコマンドで手順を確認してみましょう(以下、$TEX_JP_BUILDでローカルのtex-jp-buildリポジトリの配置位置を参照するものとします)。

$ git clone https://github.com/texjporg/tex-jp-build.git $TEX_JP_BUILD
$ cd $TEX_JP_BUILD
$ ./texjpbuild.sh

これにより、リポジトリ配下のすべてのバイナリツールのコンパイルが走ります。

texjpbuild.shの中身を見ると、これはTeX Live自身の総合ビルドスクリプト(source/Build)の薄いラッパであることがわかります。

Buildスクリプトはsourceディレクトリ内にWorkディレクトリを作成し、その中ですべてのバイナリをコンパイルします。その具体的な手順は1つ1つのプロジェクトによって少しずつ異なるはずですが、基本的にはひたすらconfiguremakeを実行していきます。ビルドによって得られた成果物はsource/instディレクトリ以下に収められます。特に実行バイナリはsource/inst/bin/<platform>/以下の位置に配置されます。

上記のtexjpbuild.shではBuildの起動オプションに--no-cleanが指定されているため、再度ビルド実行をした際には前回から差分がないものについては実際のコンパイル処理が走りません。そのため、一部だけを変更して再度ビルドする際には大幅な時間短縮が可能です。

e-pTeXの個別ビルド手順を追ってみる

さて、ただビルドするだけであれば「./texjpbuild.shを実行します」で話が終わるのですが、それだとpTeX系エンジンのビルドがどのように行われたのかが全然わかりません。TeX Live/texjporgが便利なビルドスクリプトを用意してくれているのは実用上便利ではありますが、それらのビルドスクリプトやMakefileはすべてのプロジェクトのものが統合されており巨大です。そのため、正直その中身を見ても個別の処理系の具体的なビルド手順は(少なくとも私レベルでは)さっぱりわかりません。特定の個別TeXエンジンをビルドしてみる方法はないのでしょうか。TeX Liveのドキュメントを見ると、まさにこの疑問に答えるそためのセクションがあります:

これを読むと、冒頭に残念なお知らせが書いてあります。

Unfortunately, there is one common case where the steps in the preceding section to build one package (see Build one package) do not suffice: wanting to build one, or a subset, of the TeX engines (or other Web2c programs).

要するに、個別エンジンのビルドを行うシンプルで共通した方法は存在しないということです。しかし、その続きの文章を参考にすると、一度でもtexjpbuild.shを実行した後であればこの個別ビルドはそれほど難しくないことがわかります。この場合、すでにconfigureは実行されているのでmakeだけ走らせればよいことになります。具体的には、$TEX_JP_BUILD/source/Work/texk/web2cに移動し、makeでターゲットとなるバイナリだけを引数に指定します。例えばeptexをビルドするには次のようにします。

$ cd $TEX_JP_BUILD/source/Work/texk/web2c
$ make eptex

特に差分がないとmake: `eptex' is up to date.のように言われて何も起こらずに終了してしまいますが、何かしらソースコードに変更を加えた場合はこの方法でビルドを走らせることができます。

これでようやく個別のエンジンのビルド方法がわかりました。最後の最後に、e-pTeXのビルドを題材として追いながら、そのビルドで実際に何が起きているのかを確認していきます。

なお実際のビルドログは実行コマンドだけでもひたすら長いので、適当に端折っていきます。フルのログを見たい方は、ご自身でビルドしてみてください。

WEBINPUTS=.:../../../texk/web2c TEXMFCNF=../../../texk/web2c/../kpathsea ./tie -c eptex.ch eptex.web eptexdir/eptex-base.ch eptexdir/etex.ch0 ptexdir/ptex-base.ch eptexdir/eptex.ech eptexdir/etex.ch1 synctexdir/synctex-def.ch0 synctexdir/synctex-ep-mem.ch0 synctexdir/synctex-mem.ch0 synctexdir/synctex-e-mem.ch0 synctexdir/synctex-ep-mem.ch1 synctexdir/synctex-p-rec.ch0 synctexdir/synctex-rec.ch0 synctexdir/synctex-rec.ch1 synctexdir/synctex-ep-rec.ch0 synctexdir/synctex-e-rec.ch0 synctexdir/synctex-p-rec.ch1 eptexdir/fam256.ch eptexdir/pdfutils.ch eptexdir/suppresserrors.ch eptexdir/char-warning-eptex.ch tex-binpool.ch

まず始めにtie -cコマンドで多数のchange fileが1つのchange fileにまとめられます11

WEBINPUTS=.:../../../texk/web2c AM_V_P=: /bin/sh ./tangle-sh eptex-tangle ./tangle eptex eptex

続いてtangle-shという薄いtangleのラッパ12を通してtangleが実行されます。

/bin/sh ../../../texk/web2c/web2c/convert eptex

さらにtangleが吐いたPascalコードがWeb2CによってC言語に変換されます。このconvertも単なるラッパで、ここからweb2cコマンドやfixwritesコマンドが呼び出されます。このあたりはかなりアドホックなしくみになっているようです。

ここまで来て、ようやくCコンパイラ13によるバイナリへのコンパイルが開始されます。このプロセスもなかなか複雑です。

gcc -DHAVE_CONFIG_H -I. -I../../../texk/web2c -I./w2c (中略:たくさんの依存ライブラリをインクルード) -D__SyncTeX__ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"  -Wimplicit -Wreturn-type -g -MT eptexdir/eptex-eptexextra.o -MD -MP -MF eptexdir/.deps/eptex-eptexextra.Tpo -c -o eptexdir/eptex-eptexextra.o `test -f 'eptexdir/eptexextra.c' || echo '../../../texk/web2c/'`eptexdir/eptexextra.c
mv -f eptexdir/.deps/eptex-eptexextra.Tpo eptexdir/.deps/eptex-eptexextra.Po
gcc -DHAVE_CONFIG_H -I. -I../../../texk/web2c -I./w2c (中略:たくさんの依存ライブラリをインクルード) -D__SyncTeX__ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"  -Wimplicit -Wreturn-type -g -MT synctexdir/eptex-synctex.o -MD -MP -MF synctexdir/.deps/eptex-synctex.Tpo -c -o synctexdir/eptex-synctex.o `test -f 'synctexdir/synctex.c' || echo '../../../texk/web2c/'`synctexdir/synctex.c
mv -f synctexdir/.deps/eptex-synctex.Tpo synctexdir/.deps/eptex-synctex.Po
gcc -DHAVE_CONFIG_H -I. -I../../../texk/web2c -I./w2c (中略:たくさんの依存ライブラリをインクルード) -D__SyncTeX__ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"  -Wimplicit -Wreturn-type -g -MT eptex-eptexini.o -MD -MP -MF .deps/eptex-eptexini.Tpo -c -o eptex-eptexini.o `test -f 'eptexini.c' || echo '../../../texk/web2c/'`eptexini.c
mv -f .deps/eptex-eptexini.Tpo .deps/eptex-eptexini.Po
gcc -DHAVE_CONFIG_H -I. -I../../../texk/web2c -I./w2c (中略:たくさんの依存ライブラリをインクルード) -D__SyncTeX__ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"  -Wimplicit -Wreturn-type -g -MT eptex-eptex0.o -MD -MP -MF .deps/eptex-eptex0.Tpo -c -o eptex-eptex0.o `test -f 'eptex0.c' || echo '../../../texk/web2c/'`eptex0.c
mv -f .deps/eptex-eptex0.Tpo .deps/eptex-eptex0.Po
web2c/makecpool eptex >eptex-pool.c || rm -f eptex-pool.c
gcc -DHAVE_CONFIG_H -I. -I../../../texk/web2c -I./w2c (中略:たくさんの依存ライブラリをインクルード) -D__SyncTeX__ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"  -Wimplicit -Wreturn-type -g -MT eptex-eptex-pool.o -MD -MP -MF .deps/eptex-eptex-pool.Tpo -c -o eptex-eptex-pool.o `test -f 'eptex-pool.c' || echo '../../../texk/web2c/'`eptex-pool.c
mv -f .deps/eptex-eptex-pool.Tpo .deps/eptex-eptex-pool.Po
/bin/sh ./libtool  --tag=CC   --mode=link gcc -Wimplicit -Wreturn-type -g   -o eptex eptexdir/eptex-eptexextra.o synctexdir/eptex-synctex.o eptex-eptexini.o eptex-eptex0.o eptex-eptex-pool.o libkanji.a lib/libp.a $TEX_JP_BUILD/source/Work/texk/ptexenc/libptexenc.la lib/lib.a $TEX_JP_BUILD/source/Work/texk/kpathsea/libkpathsea.la  libmd5.a $TEX_JP_BUILD/source/Work/libs/zlib/libz.a $TEX_JP_BUILD/source/Work/libs/zlib/libz.a

これでようやくeptexバイナリが得られました。

トゥルーエンド

WEB言語コワい😱


  1. DocStrip (dtx) 形式でLaTeXパッケージを作成する場合には地の文が実装コードとなるのとは対照的です。 ↩︎

  2. Homebrewユーザであればbrew install fpcで導入できます。 ↩︎

  3. 初期のMacintosh OSの実装はPascalだったようです。 ↩︎

  4. とはいえ@pの由来は “program” らしく、CWEBでも@pでCコードを書き始めてもよいようです。 ↩︎

  5. 純粋なC/C++で実装されており、もはやPascalベースのWEBシステムとは一切関係がないLuaTeXもWeb2Cにあるのはやや謎ですが。 ↩︎

  6. 昔はtex.webの冒頭部、コピーライト表記のすぐ下に “Copying of this file is authorized only if (1) you are D. E. Knuth, or if (2) you make absolutely no changes to your copy.” というちょっと面白い文言がありました。現在は “Unlimited copying and redistribution of this file are permitted as long as this file is not modified. Modifications are permitted, but only if the resulting file is not named tex.web.” となっており、やや淡々としています。たしかに昔のライセンス文だと偶然 D. E. Knuth にマッチする名前のKnuth自身ではない人物がいた場合に厄介なことになりそうですが…… ↩︎

  7. ただしライセンス上はファイル名をtex.web以外に変えさえすれば中身は自由に変更することができるので、ptex.web等の別名にすればオリジナルのファイルで開発することも可能なはずではあります。実際、北川さんの記事でも言及されているように、pdfTeXやXeTeXの実装はそうした方式で書かれています。 ↩︎

  8. この文書で言及があって気になる資料のうち、いくつかは2022年12月現在リンクが切れてしまっています。そのうち北川さんによるWEBのサンプルプログラム (ks1.web) はe-pTeX Wikiから、松山道夫『TeX読み物その3 TeXの成立ちに関する諸々』はWeb Archiveからそれぞれファイルを入手することができるようです(教えてくださった北川さん、あべのりさんに感謝します)。 ↩︎

  9. そのサブセット内のtexjporg管轄ファイルに関しては、むしろTeX Liveよりこちらのリポジトリが「上流」にあたる運用をされています。 ↩︎

  10. 当たり前ですがCコンパイラ等の開発用ツールは必要です。それらは各自で用意するものとします。 ↩︎

  11. tieコマンドの-mオプションを使用すると、change file適用後のWEBファイルを得ることができるので、ここのオプションを-mに変更することでe-pTeXの実装全体のWEBファイルを得ることができそうです。 ↩︎

  12. このラッパは処理対象ごとにtangle, ctangle, otangleのいずれかを実行するようです。XeTeXを除くナンチャラTeXのビルドを行う場合はオリジナルのtangleが実行されるはずです。 ↩︎

  13. ちなみに私のローカル環境はmacOSなので、見た目はgccですが中身はClangです。 ↩︎