HOME

スタックを使って LaTeX の数式を編集せむ

2017-4-24改訂

LaTeX で {} の多い数式を書いていると、途中で記憶力がパンクしてしまう。逆ポーランド方式(RPN)で式を書けたら、エラーがずっと減るだろう。スタックを使った対話的な編集をシェルスクリプトで実現して、emacs や vim から使う方法を考えてみた。

シェルから使う

$ sed --unbuffered -f latexmacro.sed | sed --unbuffered -f rpnedit.sed

キーボード入力されたコマンドや文字列は、マクロ集(latexmacro.sed)によって翻訳され、スタック編集用のスクリプト(rpnedit.sed)に渡される。rpnedit.sed は入力があるごとに自分が保持しているスタックを更新し、その新たな内容を出力したうえで、次の入力を待つ。

次はこの仕組みを使い、ターミナル上で \(\frac{1+2}{3}\) を作成しているところ。

このままではあまり愛嬌がないが、emacs や vim のスクリプトから呼び出して利用すると、なかなか効率的&愉快に LaTeX の数式が編集ができると思う。以下はその例。

Emacs から呼び出す

次はスタックの内容を一気に受け取り、新しい状態のスタックを出力するバッチ処理用スクリプト。コマンドは最も浅いレベルにあるものだけが実行される(他のレベルにあるコマンドは、たんに文字列として扱われる)。rpnedit.sed の出力を -n オプションで抑止し、スタックの最後の状態だけを rpnedit.sed の print コマンドで明示的に出力している。なお最後の tail は出力先頭につく不要な改行を除去するもの。

#!/bin/sh
rpneditdir="PATH/TO/YOUR/DIRECTORY/"
sed -e '$! s/.*/"&"/;$ s/$/\nprint/' |
#sed -e 's/^$/""/;$ s/$/\nprint/' |
    sed -f $rpneditdir/latexmacro.sed |
    sed -nf $rpneditdir/rpnedit.sed | tail -n +2

(上のスクリプトの 3 行目を、コメントアウトしてある 4 行目によってさしかえると、入力が一行ずつ順次なされたのと同じ結果になる。つまり、いずれのレベルにあるコマンドも実行される)

次は emacs lisp のコマンド。空行(なければバッファ先頭)から現在のポイントまでを、上記のスクリプト(rpnbatch.sh)を使って書き替える。

(defun rpn-latex ()
  (interactive)
  (let ((end-of-stack (point))
	(beginning-of-stack
	 (or (prog1 (and (re-search-backward "\n\n" nil t)
			 (goto-char (+ 2 (point)))))1)))
  (shell-command-on-region
   beginning-of-stack end-of-stack "/YOUR/PATH/TO/rpnbatch.sh" nil t)
  (goto-char (region-end))))

次の動画は \(\lim_{n\to \infty}\left(1+\frac{1}{n}\right)^{n}\) という式を作成しているところ。新しい行を入力するたびに rpn-latex コマンドを使っている。

vim から呼び出す

vim から起動されたシェルスクリプトを ^D で終了させると、終了直前に rpnedit.sed の write コマンドが発行され、スタックの最後の状態が一時ファイル rpnedit.tmp(ファイル名は固定)に書き出される。vim はバッファにこれを読み込む。なお、対話画面の見栄えがよくなるように、標準出力を整形する awk のスクリプト(printlevel.awk)を使っている。

以下は、私の .vimrc の一部で :Rpn にシェルスクリプトを呼ぶ関数を割り当てている

function! RpnLatex()
   let l:rpnedit_path = "YOUR/PATH/TO/rpnedit.sed"
   let l:latexmacro_path = "YOUR/PATH/TO/latexmacro.sed"
   execute "silent !" . "sh -c \"trap 'echo write' 0; cat\" | sed --unbuffered -f " . l:latexmacro_path ." | sed --unbuffered -f " . l:rpnedit_path . " | awk -f YOUR/PATH/TO/printlevel.awk -v \"statusline=--RpnLatex-- press ^D to exit\" -v \"ttyrows=" . screenrow() . "\""
   execute "r!" . 'sed -ne '':a;/\[END_OF_STACK\]$/\!{N; ba};/\[END_OF_STACK\]$/{s/\n\[END_OF_STACK\]$//; h};${g;p}'' rpnedit.tmp'
endfunction
command! Rpn : call RpnLatex()

次の動画は \(\lim_{n\to \infty}\left(1+\frac{1}{n}\right)^{n}\) という式を作成しているところ。

登場したスクリプトたち

rpnedit.sed

latexmacro.sed

printlevel.awk

おわり