マクロツイーター

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

TeX なのに文字列をクオートする話(1)

ごく一般的なプログラミング言語の多くでは、文字列のリテラルをクオート(ダブルクオーテーション、"...")で囲った形で表す。

[hello.mp]

% "TeX-comedian" や "Hello, " が文字列リテラル
string person; person = "TeX-comedian";
message ("Hello, " & person & "!");
end

マークアップ言語においても、メタデータの文字列の“リテラル”はクオートで囲む形式で表すことが多い。

TeX なのでクオートで文字列しない話

ところが、TeX 言語では、元来は、「文字列をクオートで囲って表す」という文法は存在しない。これを知らないと余計なことで悩むことになる。

\def\somemacro#1{%
  \ifx"#1"% #1が空ならば
    %...(以下省略)
}

空列判定をするお馴染みの技法であるが、ある TeX 言語学習者がこれを見て、

  \ifx#1""% #1が空, つまり""と等しいならば!

の書き間違いではないか、と疑問に感じている場面に出会ったことがある。もちろん、この推測は正しくない。まず何より、「TeX 言語では "" で“空文字列”を表すという文法自体が存在しない」のである。

TeX 言語において、“文字列の値”が必要な場合は普通のトークン列で表すのが通例である。そしてトークン列はそれ自体は囲い文字を持たない。(例えば「Hello」という“文字列”は Hello というトークン列で表される。)

無論、これだけでは「“文字列の値”を引数にするプリミティブやマクロ」が困ることになる。例えば、「\foo Hello world」では \foo の後に続くトークン列のどこまでが \foo の引数なのか判らない。従って、TeX 言語では「トークン列を波括弧で囲ってグループ化する」という文法を持っているのである。すなわち、「\foo{Hello} world」であれば、\foo が引数とする“文字列の値”は「Hello」だと判断できる。

そういうわけで“文字列の値”を引数にとる TeX のプリミティブ(例えば \write\errhelp など)は波括弧でその“引数の範囲”を示す規則になっている。

% \write の引数は "Hello" で, "world" は入らない
\write16{Hello} world

TeX なのに文字列を波括弧しない話

ところが、ファイル名を表す文字列については扱いがチョット異なる。

\input foo
\openout15=bar.txt
\openin2=baz.aux

このように、ファイル名を引数にとるプリミティブについては、その引数を波括弧で囲わない規則になっている。((なお、LaTeX\input 命令は LaTeX カーネルで定義されたマクロであって、プリミティブの \input とは別物である。もちろん、LaTeX\input は内部でプリミティブの \input\@@input と改名されている)を利用している。))それでは、どうやって引数の終わりを判断するかというと、「空白文字で終結となっている。*1

% foo.tex が読まれて実行された後、"bar" が出力される
\input foo bar

つまり、(元来の)TeX 言語では、「ファイル名は空白を含まない」ということを前提にしていたのである。ちなみに、「\input{foo} bar」とすると「{foo}.tex」というファイル名を指定したことになる。

同じ理由で、\font プリミティブの引数も「空白で終結」する規則である。(元来の)TeX では「フォント名」とは TFM ファイルのベース名であり、それは空白を含まないからである。

\font\ahofont=cmfi10

TeXLaTeX が作られた当初ではそもそもファイルシステムで空白文字を含む名前は許されなかった。その後の十数年間も、空白を含むファイル名は一部のシステムで使えても“可搬性のない”もので、TeX のような可搬性を重んずる処理系で使えないのも当然と見なされていた。*2

TeX なのにクオートで文字列する話

しかし、今では全ての主要な OS のファイルシステムが空白入りのファイル名を許すようになっていて、実際に空白入りのファイル名の使用はごく普通のことになっている。そんな中では、「空白入りのファイル名は使えない」という仕様は、「普通の人」にとってはもはや受け入れ難いものになっているのは確かである。

では TeX はどうなのかというと……、実は、

今の TeX エンジンは空白入りのファイル名を扱える

である。*3

実際に確かめてみよう。

Hello {\TeX} world!\bye

というファイルを作って、これに「hello tex world.tex」という空白入りのファイル名を付けたとする。そして「このファイル名を引数にして tex コマンドを実行」する。そのコマンド行の書き方はシステムにより異なるが、UNIX 系のシェル(sh、bash、等)や Windowsコマンドプロンプトでは次のようになる。

tex "hello tex world"

すると、何事もなくコマンドが終了し、DVI ファイル「hello tex world.dvi」が生成される。((もちろん、「dvipdfmx "hello tex world"」で PDF 文書に正常に変換できる。))

This is TeX, Version 3.14159265 (TeX Live 2015/W32TeX) (preloaded format=tex)
 encTeX v. Jun. 2004, reencoding enabled.
(./hello tex world.tex [1] )
Output written on "hello tex world.dvi" (1 page, 232 bytes).
Transcript written on "hello tex world.log".

ここではオリジナルの TeX を試したが、pTeX や pdfTeX などの他の拡張エンジンも同様に空白入りファイル名を扱える。*4

さて、TeX で空白入りのファイルが扱えるとなると、当然、TeX ソース中で \input 等を利用する場合でも空白入りのファイルを指定する必要が出てくる。しかし、困ったことに、ファイル名を引数にするプリミティブは「空白で終結する」という規則になっていてこのままでは対応できない。結局、TeX 実装者*5が選んだ方法は……そう、

クオートで囲む

だったのである。

\input "hello tex world"

TeX の文法としては、「\input{hello tex world}」のように「波括弧で囲う」という規則にした方が一貫していて好ましい気もする。しかし、恐らく「波括弧を含むファイル名」に関する互換性が失われるのを避けるために、敢えて「クオートで囲う」規則にしたのだと思う。*6

\font プリミティブについては、XeTeX において OpenType フォントが直接(TFM を介さずに)扱えるようになり、また OS にインストールされた OpenType フォントが“フォント名”で呼び出せるようになった。OpenType のフォント名は「Times New Roman」のように空白を含むものがごく普通に存在する。そのため、\font プリミティブも「クオートで囲む」規則が適用されている。((OpenType を扱えない従来の TeX エンジンでは「空白を含むフォント名」は正当ではありえないが、一応 \font プリミティブには「クオートで囲む」規則が適用されているようである。))

\font\ftmr="Times New Roman"

TeX なのに波括弧でファイル名する話

面白いことに、LuaTeX エンジンでは、\input 等のファイル名の引数や \font のフォント名の引数において「波括弧で囲む」という規則が適用されている。*7LuaTeX は新しいエンジンなので(波括弧入りのファイル名に関する)互換性を考える必要が無かったので一貫性のある方法を採用したのだろう。

\input{hello tex world}
\font\ftmr={Times New Roman}

*1:引数の終結のための空白トークンは吸収される。なお、展開不能な非文字トークンがある場合も引数は終結する。

*2:TeXコマンドラインの文化で育ったものであり、コマンドラインでは空白を含むファイルは扱いが煩雑で嫌われる、という側面もあった。

*3:少なくとも TeX Live 2013 以降では大丈夫である。

*4:LuaTeX は少し難があるが、その話はまた後日に。

*5:恐らく、一番最初に「空白入りファイル名対応」を行ったのは pdfTeX だと思われる。

*6:UNIX では「クオートを含むファイル名」も許されるが、コマンドシェルでの扱いが煩雑なため、実際に使われることはほとんど無い。

*7:他のエンジンと同じ「クオートで囲む」規則も存在する。