TeX by Topicゼミ (3) uppercaseとlowercase

2017-11-15 (updated: 2025-12-09) #TeX

前回はTeXのカテゴリーコードに関わる話を網羅的にまとめました。今回は少し分量を抑えて、2つのプリミティブ\uppercase\lowercaseに焦点を絞ってみたいと思います。

基本的な使い方

\uppercase, \lowercaseは文字通りそれぞれトークン列の「大文字化」と「小文字化」を行うプリミティブです(「大文字化・小文字化」の厳密な意味については次節で解説します)。

\uppercase{Foo} %=>FOO
\lowercase{Bar} %=>bar

これだけならまるで「普通のマクロ」のように使うことができそうに思えますが、実際にはこれらのプリミティブはもう少し特殊な挙動を示します。すなわち、これらの命令は「展開されて、引数を大文字化・小文字化したトークン列を生成する」のではなく「実行すると、引数の中で大文字化・小文字化すべき文字トークンの文字コードを別の文字コードに変換する。一方、文字トークン以外のトークンやすべての文字トークンのカテゴリーコードは変更しない」ということを行います。

このことは、言葉で説明しても少々わかりにくいと思うので、下にいくつか例を挙げてみます。

例1:\expndafterとの連携

マクロ\foo

\def\foo{foo}

のように定義されている状況で\uppercase\fooを用いて“FOO”という出力を得たい場合を考えます。普通に

\uppercase{\foo} %=>foo

とすると、\uppercaseの引数内の\fooは制御綴(=文字トークンではない)なので、\uppercaseは特に何の効果ももたらしません。一方で、\uppercaseは展開可能なマクロではないので、通常のマクロに対する展開制御のように\uppercaseの前に\expandafterを置いても無意味です。

今のケースで目的を遂げるためには\uppercaseの直後に\expandafterを1つ置くとうまくいきます。

\uppercase\expandafter{\foo} %=>FOO

例2:\csnameとの連携

\uppercaseは(大文字化すべき)文字トークンを大文字化する一方で、制御綴はそのままにするので

\uppercase{\csname foo\endcsname}

のように書くと\FOOと書くのと同様の効果が得られます。

一方\csname ... \endcsnameの間には展開できないトークンを置くことはできないので、当然「実行される」プリミティブである\uppercaseも置くことはできません。したがって次のコードはエラーになります。

\csname\uppercase{foo}\endcsname %<-error

\uccode\lccode

これまで何度か「大文字化・小文字化」という表現を使ってきましたが、ある文字コードをどんな文字コードに大文字化あるいは小文字化するかはTeX処理系のbuilt-inな設定で決められているわけではありません。大文字化・小文字化の操作で、どの文字コードを何の文字コードに変換するのかは、それぞれ\uccode, \lccodeというプリミティブで指定することが可能です。

\uccode<code>=<uccode>
\lccode<code>=<lccode>

この設定によって、文字コード<code>に対応する文字は\uppercase利用時は<uccode>の文字に、\lowercaseの場合は<lccode>の文字に変換されます。ただし<uccode><lccode>が0に設定されている場合、変換は行われません。

\lowercaseトリック

さて、ここからやっと本稿の本題に入ります。これまで見てきたように\uppercase, \lowercaseは「カテゴリーコードはそのままにして、文字コードだけを別のものに変換する」という動作をします。このことを利用すると、通常の方法では作ることが難しい「特殊な文字トークン(文字コードとカテゴリーコードの組)」を作り出すことができます。

応用例1:\newifの定義

\lowercaseトリックは、有名なところでは\newifマクロを定義する際に利用できます。ご存知のように、\newif命令は

\newif\ifhoge

という記述によって

\def\hogetrue{\let\ifhoge=\iftrue}
\def\hogefalse{\let\ifhoge=\iffalse}
\hogefalse

と等価な働きをする命令です。このマクロを定義するためにまず\if@という命令を定義します。

{\lccode`1=`i \lccode`2=`f \lowercase{\gdef\if@12{}}}

1および2のカテゴリーコードは12なので、上の\gdef\if@12{}において12の部分はカテゴリーコード12のトークン列です(したがって、この部分は\gdefのパラメタテキストとなります)。これが\lowercase の効果によって、カテゴリーコードは12のまま文字コードだけがそれぞれifのそれになります。すなわち、\if@は直後に続くカテゴリーコード12のifという文字トークン列を消し去るマクロです。

あとは、\newif命令本体と内部マクロ\@ifの定義を見ればその使用法がわかります。

\def\newif#1{\count@\escapechar \escapechar\m@ne
  \expandafter\expandafter\expandafter
    \def\@if#1{true}{\let#1=\iftrue}%
  \expandafter\expandafter\expandafter
    \def\@if#1{false}{\let#1=\iffalse}%
  \@if#1{false}\escapechar\count@}
\def\@if#1#2{\csname\expandafter\if@\string#1#2\endcsname}

重要な部分を抜粋して解説すると、まず\newifの定義の1行目で\escapecharが-1に設定されていることがわかります。その上で\@ifの定義を見ると\string#1の展開結果を先ほどの\if@命令に与えています。ここで#1\newif\ifhogeの形で与えられる\ifhogeなので、\stringの効果によってカテゴリーコード12のトークン列ifhoge\if@の後に続くことになります。つまり、このトークン列の冒頭if\if@命令によって消去され、最終的に\@if命令は\hogetrue\hogefalseという制御綴に展開されるわけです。

応用例2:任意の文字コードを出力する

\lowercaseトリックは狙った特殊な文字トークンを作り出すという用途のみならず、任意の文字コードを外部に出力したい場合などにも役立ちます。

以下は、TeX by Topicに掲載されている整数レジスタ\mycountに格納されている値の文字コードを端末に出力するシンプルな例です。

\lccode`a=\mycount \chardef\terminal=16
\lowercase{\write\terminal{a}}

拙作のWhitesnowmanパッケージでは、このテクニックをより汎用的な形で使うため、次のようなサブルーチンを定義しています。

\begingroup
  \catcode0=12\relax
  \global\def\chrdef#1#2{%
    \begingroup\lccode0=#2\relax
    \lowercase{\endgroup\def#1{^^@}}}%
\endgroup

プリミティブ\chardefで文字コードが代入された制御綴(「chardefトークン」といいます)は実行プロセッサによって処理されるものなので展開することができません。一方、この\chrdefマクロは\chardefに近いシンタックスで用いることができますが、定義される制御綴の実体がマクロとなるので「展開可能なchardefトークンもどき」を作ることが可能です。