読者です 読者をやめる 読者になる 読者になる

LISP on TeXを作る(データ構造編)

TeX

この記事はTeX & LaTeX Advent Calendar 2013の12/25担当分です. < 12/24 は munepiさん || 2014/12/1は\@undefined

はじめに

TeX & LaTeX Advent Calendar 2013のトリはLISP on TeXとなった.

今年はTUG2013での発表という大イベントがあった.そのときはなるべく「急カーブ注意」なことは避けてきたのだが,標識3個くらいまでならむしろやったほうが良かったのではないかと後悔したのが記憶に新しい*1

今日はそのリベンジとして,LISP on TeXの中身を説明していこうと思う*2.今回はデータ構造にのみ言及する.TeXでプログラミングしたいキチガイコアな人からLISP on TeXの型を拡張したいという変態まで,参考になれば僥倖である*3

LISP on TeXのデータ構造の基本形

LISP on TeXにおいて,データ構造は非常に単純な構造になっている.型xxxのデータ...は次で表現される.

\@tlabel@xxx{...}

ここで\@tlabel@xxxは型を表すラベル(以後,型ラベルと呼ぶ)である.例えば,\@tlabel@intは整数型,\@tlabel@stringはTeXトークン列を示す.これらはすべて\ifxにより識別できるようになっている.*4一応注意しておくと,これらはスタイルファイル中の記述なので,「@」のカテゴリーコードは11である.波括弧の中身はデータ本体である.例えば,整数の10(LISP on TeXリテラル表記は:10)はパーサによって\@tlabel@int{10}と認識される.

ここで,型と呼ぶものは普通のプログラミング言語のそれとは異なる.実は,\defineや\lambdaなどのスペシャルフォームもそれぞれ固有の型を持つ.例えば,\defineに束縛されているデータは\@tlabel@define{}となっている.

型ラベルの展開と評価器

ただ単に型のラベルとして制御綴りを使用するのはもったいないので,型ラベルを展開するとeval(式の評価)やapply(関数などの適用)に使用できるマクロが手に入るように構成されている.型ラベル\@tlabel@xxxは展開すると,

\@eval@xxx\@apply@xxx\@@apply@xxx

となるようになっている.

\@eval@xxxは,パターンマッチなしの3引数を取るマクロである.第1引数は型xxxのデータ本体が,第2引数はevalが呼び出されたときの環境(変数とデータの対応)が,そして第3引数には制御綴りが与えられる設計になっている.第3引数の制御綴りはevalした結果の格納先である.\@eval@xxxは第1,2引数をもとに型に対応したevalを実行し,第3引数の制御綴りをグローバルに書き換える*5ことによって評価をおこなう.例えば,整数の評価規則\@eval@intの定義を見ると,

\show\@eval@int
> \@eval@int=macro:
#1#2#3->\gdef #3{\@tlabel@int {#1}}.

のように,制御綴り#3を\@tlabel@int {#1}に定義していることがわかる.

\@apply@xxxと\@@apply@xxxはどちらもapply用に使用されるマクロである.引数には基本,どちらも同じパターンマッチ#1#2\@#3#4が使用される.#1はデータ本体,#2は与えられた引数のリスト,#3は評価時の環境,#4が結果格納先の制御綴りである.ただし,型によっては#2の部分でより細かいパターンマッチが適用される.例えば,\defineの第1引数は絶対にシンボル(\@tlabel@symbol)でないとならないので,その辺がパターンマッチで表現されている.2つの違いは,前者が関数呼び出しで起動されるのに対し,後者がapply関数による関数適用で使用されるという点のみである.すなわち,前者は引数に対して何らかの処理(例:引数の評価)を行うのに対し,後者はそれを行わない.正確な説明は(おそらくやるであろう)評価器編で行う.

余談

この構造のメリット・デメリット

この構造により,LISP on TeXでは\if系トークン(\ifや\ifxなど)の使用を極力避ける仕組みになっている.\if系トークンの挙動は割りと面倒なので,この構造にしておくと何かと実装に便利である.しかし,この構造は同時に評価器のコードを散逸させてしまっているデメリットを持つ.実は,評価器のメイン部分は\@eval@consと各種適用可能な型の\@apply@xxxと\@@apply@xxxなのだが,これらのコードが分離してしまっているため,コードの変更には弱かったりする.

LISP on TeXへの型の追加

先の規則さえ満たせば,ユーザがあとから型を追加することが割と容易になっている.実際,\@eval@xxxは

\@eval@xxx#1#2#3{\gdef#3{\@tlabel@xxx{#1}}}

としておけばevalした結果はそれ自身であるという実装が完成し,applyに関しては,(ユーザがapply可能な型を新設するのは稀なことであろうことから)展開時にエラーメッセージを出力するマクロにしておけば問題ない.実際,LISP on TeXのすべての組み込み型はそのような初期化が行われる.また,パーサへのフック機構も用意してある(参考).説明は(おそらく次回の)パーサ編に回すが,このコードさえ理解してしまえば,ユーザはほぼ自由にLISP on TeXに型を追加できるようになる.

終わりに

なんか急カーブ注意の標識が何本立つのかよくわからない記事になってしまったが,
Happy TeXing and Merry Christmas!
%
%
\Finale

*1:奥村先生に「中身が気になる」と言われたのです

*2:英語でない理由は察して……

*3:範囲狭い……

*4:実装では\ifxで区別しているところと,マクロのパターンマッチ使っているところがあったりするが……

*5:正確に確かめたわけではないが,グローバルでなくとも動く可能性はある