マクロツイーター

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

TeX でもっともっと Quine する話(2)

前回の続き)

それでも DVI で Quine したい話

だがしかし完全に希望が無くなったわけではない。「DVI で Quine」が無理なのは、飽くまで“普通の”plain TeXLaTeX においての話に過ぎなくて、TeX のフォーマットは他にも色々ある。

例えば画期的なフォーマットである「1TeX」でなら、0xF7 も 0x02 も 0x01 も全て(文字「1」(0x31)ではないので)無視されるので、先頭の「固定のバイト列」をやり過ごせる。なのでこの画期的な「1TeX」を使えば Quine が作れるかも知れない。ただこの方法には大きな問題がある。1TeX は確かに画期的ではあるが知名度が(残念ながら)不十分で、従って 1TeX で Quine を作ってもインパクト的にイマイチかも知れない。なので、「1TeX の DVI で Quine する話」は賢明なる読者への課題として残しておくことにして、ここでは別の手段を講じる。それは“普通でない”plain TeX を使うことである。

本ブログの愛読者であれば、真に前衛的な TeX フォーマットである「スヤァTeX」のことはご存知であろう。「スヤァTeX」では入力ファイル中の「( ˘ω˘ )スヤァ…」が「\relax」に置き換えられて解釈されるわけだが、このように現在の TeX エンジンには「入力テキストを加工する」という機能が備わっている。これを利用すれば、「0x01 を別のバイトに置き換えてエラーを回避する」ことができそうである。ところが残念ながら、「スヤァTeX」で利用している「encTeX 拡張」(これについては以前の記事で解説を行った)は既定では無効になっていて、これを有効化できるのはフォーマット作成時に限られる。だからこの機能を利用するのでは、「1TeX」よりもっとマイナーなフォーマットを作る羽目に陥ってしまう。

TCX ファイル

実は TeX エンジンには「入力テキストを加工する」ための別の機構が存在する。それが「TCX ファイル(TeX character translation file)」である。

encTeX 機能と同じく TCX ファイルも「文字コードの変換」を本来の目的とする。例えば、入力の文字コードを Latin-1、フォントエンコーディングを T1 にしてドイツ語の plain TeX 文書を作りたいとする。

[test.tex]
% This file is encoded in Latin-1.
\font\lmr=ec-lmr10 \lmr
Es lädt\dots. Weißt du?\bye

これを普通に「tex test」でコンパイルすると、出力は次のようになる。〈ä〉はいいが〈ß〉はおかしい。

〈ä〉の符号位置は Latin-1 と T1 のどちらでも 0xE4 であるが、〈ß〉は Latin-1 では 0xDF、T1 では 0xFF と食い違っているのである*1。TCX ファイルを用いると、このような入出力の間の文字コードの違いを吸収することができる。今の場合、次のようなファイルを作成する。

[eszett.tcx]
% 0xDF を 0xFF に置換する
0xdf 0xff

TCX ファイルを利用するにはコンパイル時に -translate-file スイッチでファイル名を指定する。((TCX ファイルの検索パスは「$TEXMF/web2c」(Kpathsea のファイルタイプは“web2c”)でありカレントは含まれないので、カレントにファイルを置いた場合は「./」を指定する必要がある。))

tex -translate-file=./eszett test

TCX ファイルを指定した場合、次のような正常な結果が得られる。

TCX ファイルでどうやって Quine するか

前回の記事で、“普通の”plain TeX では 0x01 のバイトが邪魔になって Quine できない、という話をした。TCX ファイルを使えば、この難題が解決できることは明らかだろう。つまり、0x01 を何か別の(カテゴリコード12の)文字に置き換えるような TCX ファイルを用意すればよい。しかも、encTeX 拡張とは異なり、TCX ファイルの指定は(フォーマット作成時でなく)通常の実行時に行えるので、独自のフォーマットを使う必要が無くなるというメリットがある。

TCX を使うと決めたのでもう少し積極的に利用してみる。以下のような方針をとる。

  • 「print 文」に相当するものとして、\special プリミティブを使う。(\special の引数の文字トークン列は、DVI の special 命令の引数としてそのまま書き出される。)
  • 「固定のバイト列」の先頭にある 0xF7 を TCX を用いて 0x25(%)に置き換える。その上で \special で CR(0x0D)を出力してこれが DVI 中での最初の改行文字になるようにする。
  • 出力の DVI(の“本文領域”の内容)を special 命令だけにするために“出力ルーチンの乗っ取り”を行う。
(続く)

*1:T1 の 0xDF は「0xFF の大文字」に相当する符号位置なので、“〈ß〉の大文字”である〈SS〉の単一グリフ(!)が入っている。