Gauche は今では R7RS 準拠の Scheme 処理系を名乗っているが、それ以前は R5RS に準拠していた。 しかし、 R5RS の仕様の内で Gauche (の作者) が意図的に無視していた箇所がある。 そのひとつが transcript-on
/ transcript-off
だ。 これらは処理系との対話をファイルに記録する機能の開始・終了を指示する手続きであり、 Gauche では提供されなかった。 Emacs などの開発環境を利用していればその機能で記録を残すことが出来るので言語処理系として提供する必要がなかったということもあるのだろう。
そのかわり read-eval-print-loop
という手続きが提供されていて、カスタマイズされた repl を作ることが出来るようになっている。
Gauche ユーザリファレンス: read-eval-print-loop
読み込み手続きや印字手続きがその本来の動作のついでにファイルに記録するようにすれば、処理系が transcript-on
/ transcript-off
手続きを提供するより自由度の高い記録処理が可能だ。 Gauche を対話モードで起動したときにも内部的には read-eval-print-loop
手続きが使われている。
さて、そこで今回は Gauche に transcript-on
/ transcript-off
を追加することを考えた。 .gaucherc に追加して利用することを想定している。 .gaucherc については Gauche のドキュメントを参照して欲しい。
以下が実装だ。
(with-module gauche.interactive (define %repl-print (with-module gauche.internal %repl-print)) (define (my-printer . vals) (apply %repl-print vals)) (define (my-reader) (%reader)) (define (my-prompter) (%prompter)) (define-in-module user (read-eval-print-loop :optional (reader #f) (evaluator #f) (printer #f) (prompter #f)) (let ([reader (or reader my-reader)] [evaluator (or evaluator (with-module gauche.interactive %evaluator))] [prompter (or prompter my-prompter)] [printer (or printer my-printer)]) ((with-module gauche read-eval-print-loop) reader evaluator printer prompter))) (define (%transcript-off) (error "Transcript mode not yet.")) (define-in-module gauche transcript-off %transcript-off) (define-in-module gauche (transcript-on filename) (unless (eq? transcript-off %transcript-off) (error "Already in transcript mode.")) (let ((port (open-output-file filename :buffering :line)) (old-printer %repl-print) (old-reader %reader) (old-prompter %prompter)) (set! %repl-print (lambda vals (for-each (lambda(e) (write e port) (newline port)) vals) (apply old-printer vals))) (set! %reader (lambda () (rlet1 s (old-reader) (write s port) (newline port)))) (set! %prompter (lambda () (rlet1 s (with-output-to-string old-prompter) (display s) (flush) (display s port)))) (set! transcript-off (lambda() (close-output-port port) (set! %repl-print old-printer) (set! %reader old-reader) (set! %prompter old-prompter) (set! transcript-off %transcript-off) (undefined))) (undefined))) )
では利用してみよう。
saito ~ $ gosh gosh> (transcript-on "test.txt") #<undef> gosh> (+ 1 2) 3 gosh> (sin 0.5) 0.479425538604203 gosh> (transcript-off) #<undef> gosh> (exit) saito ~ $ cat test.txt #<undef> gosh> (+ 1 2) 3 gosh> (sin 0.5) 0.479425538604203 gosh> (transcript-off)
transcript-on
を実行してから transcript-off
を実行するまでの対話がファイルに書き込まれているのがわかる。 ここでは repl に与えた式と戻り値を記録しているので display
や write
の出力結果は記録されないが必要であれば捕捉することは可能だろう。
Document ID: a06002e461443df32d67dd0a18838c33