2010年7月18日日曜日

Emacs LispでファイルIO

Emacs Lispで、ファイルから入力する処理をCommonLispのノリで書こうとしたら・・・

>(require 'cl)
>(with-open-file (in "hoge.txt" :direction :input)
(with-open-file (out "fuga.txt" :direction :output)
(princ (read in) out)))
Debugger entered--Lisp error: (void-function with-open-file)

あれ?

>(open "hoge.txt")
Debugger entered--Lisp error: (void-function open)

・・・あれ?

ELispってファイルオープンしてストリームを作ることができないのか・・・。

調べて見たところ、streamとして使えるのは以下のとおりらしい。

  • 入力
    • buffer
    • marker (バッファ内のマーカの位置)
    • string
    • function (2種類の呼び出し方を扱えるもの)
    • t (ミニバッファ)
    • nil (standard-input)
    • symbol (関数定義)
  • 出力
    • buffer
    • marker (バッファ内のマーカの位置)
    • function (1つの文字を引数にして呼ばれる)
    • t (エコー領域)
    • nil (standard-output)
    • symbol (関数定義)

つまりはバッファを介さないとファイル入出力ができないっぽい。

なので、open/closeでストリームを扱っているつもりになれる関数を書いてみた。


(require 'cl)

(setf *opening-stream-buffers*
(make-hash-table))

(defmacro with-default-values (binds &rest body)
`(progn
,@(mapcar
(lambda (bind)
`(unless ,(first bind)
(setf ,(first bind) ,(second bind))))
binds)
,@body))

(defun open (filename &optional direction)
(with-default-values
((direction :input))
(let ((buf (create-file-buffer filename)))
(when (eq direction :input)
(with-current-buffer buf
(insert-file-contents filename)))
(setf (gethash buf *opening-stream-buffers*) (list direction filename))
buf)))

(defun close (buf)
(when (eq (first (gethash buf *opening-stream-buffers*))
:output)
(with-current-buffer buf
(write-region
(point-min) (point-max)
(second (gethash buf *opening-stream-buffers*))))
(kill-buffer buf)))

0 件のコメント:

コメントを投稿