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

ユーザ定義マクロのみを展開する手法に関する考察(1)

はじめに

こんな話が出ていた.

面白そうなので,実際に作成してみることにした.

中間報告

で,作ってみたのがこれ.

github.com

実行してみると,ユーザ定義マクロが展開され,マクロの定義が消失することがわかる.アイデア自体は簡単で,次を実行するプログラムを作成しただけである.

  1. \@argdef を書き換えることにより,ユーザ定義マクロの定義を取得(header.texの内容)
    • 要は\newcommandの内側をフック
    • 結果はgathergetuser.texに書きこまれる
  2. 元文書をの各行について,次を処理($\mbox{expander.texの内容} + \alpha $)
    1. \documentclassなど,LaTeXが定義しているマクロに\relaxを代入
      • 例外的に\newcommandはコメント行を出力するようなマクロに再定義
    2. 1で取得したユーザ定義マクロを再定義
    3. 対象の行を\edefにより展開
      • このタイミングでユーザ定義マクロのみが展開される
    4. 得られた結果をファイルに出力

各行ごとに処理しているのは,改行を保持するためである.

考察

一見するとうまくいっているように感じるこの手法だが,実はうまくいかないケースが多い.かなり数が多いのだが,いくつか例を挙げておこう.

展開しないコントロールシーケンスの列挙が面倒

展開したくない制御綴りはexpander.texで列挙している.今回の試作ではサンプルを通すための必要最低限しか定義していないので,このあたりのメンテナンスがつらい.また,各種(自作以外の)パッケージ由来のマクロも展開してはいけないはずなので,このあたりも考慮して展開してはいけないマクロ一覧を列挙する必要がある.

実は,「展開したくないもの」よりも「展開したいもの」を列挙する方が圧倒的に楽なので,文書中の全トークンをなめて,展開したいものならば展開し,そうでないならそのままというような処理の方が適切であるように思える*1

完全展開してはいけないコンテキスト

verbatim環境の中など,そもそもユーザ定義マクロであろうとも展開してはいけないケースがあるが,現在の手法ではそれに対応していない.そのようなコンテキストはそんなにないはずなので,その部分のみ特殊な展開処理をしてやる必要がある.

ユーザ定義マクロを完全展開することが不適切な場合

ユーザ定義マクロが副作用を含む場合,単純な完全展開では求める結果が得られない.ユーザ定義マクロを安全に展開できるのは1段階のみである.しかし,ユーザ定義マクロが別のユーザ定義マクロを使用している場合,1回の展開ではそれを除去しきれない.

このあたりは,「ユーザ定義マクロは完全展開しても安全なものに限定する」,「ユーザ定義マクロがある程度残ることを無視して1回の展開に限定する」などの対策しか思い浮かんでいない.

展開が複数行にまたがる場合

今回は改行を保存するために1行ごとに処理しているが,そもそも展開に複数行使う場合がある.

TeXのエラーをハンドリングして,エラーが出なくなったらその出力を採用する」という方法や,「そもそも文書全体を処理した後,改行を何らかの方法で復元する」,「改行をあきらめる」などが考えられる.

まとめ

まだまだである.なので,別アプローチで試作している.

*1:これを実現するためにはTeX芸人検定2段が必要になる