前回は 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のまま文字コードだけがそれぞれ i
と f
のそれになります.すなわち,\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 トークンもどき」を作ることが可能です.