slimv(4)
年初にFreeBSD 9 がリリースされていた。これってお年玉なんだろうか? 遅ればせながら 落としてきて入れてみた。だって今使ってるのは賞味期限切れの8だもの。
インストーラーがLinuxとの対抗上グラフィックバージョンになったらしいけど、VMWARE上の コンソールでは当然ながら、そんなのは拝めず。これって初日の出を見逃して悔しがる気持ちと 、ちと似てるかなあ。
インストール手順が多少変わってステップが省略されていたけど、迷わずにインストール出来た。 ここの所、色々な物を入れて楽しむってのをしなくなっていたんで、久しぶりに楽しめたよ。
仮想HDDには20Gを割付、パーテションはルートとスワップのみと言う横着仕様にした。 FreeBSDもDebianみたいにバイナリーアップデートが出来るようになってるんだけど、それの 実施時一時的に特定パーテションを使うそうな。それを見越してパーテションを切れなんて、 否現実的ですからねぇ。(退化モードに入ってるな)
portsと srcを入れた状態で、1.6Gぐらいになった。後は暇に任せて、使うパッケージを入れ、環境が 整った所で、8系からホームの引越しだな。引越しはアート引越しセンター0123へお願いすればやってくれるのかな?
取り合えずWindowsからリモートログイン出切ればいいんでGUI系なアプリは後でもいいか。 psしたら、使いもしないメールサーバーがデフォで起動してたんで止めておかないとな。 それから、不要なシリアルログイン用のプロセスも起動してるな。これ止めるには、/etc/ttysを 編集すれば良かったのかな。すっかり忘れているぞな、もし。使う時だけbootするマシンにcronは 必要だろうか? しばし悩んだのは秘密だ。
そうそう、マシンを落とす時、いちいちrootになるのかったるいので、自分のアカウントをオペレータ所属に しておこう。
csi用slime
折角の機会なのでscheme用slimeのコードを眺めてみる。まずは鶏さん用のやつね。
;; Send list `msg' as a reply back to SLIME. (define (swank-write-packet msg out) (let* ((string (with-output-to-string (lambda () (write msg)))) (pad-hex (lambda (n) (pad-char #\0 (pad/left 6 (num n 16))))) (packet (fmt #f (pad-hex (string-length string)) string))) ;; This may be called by code that has set print-length-limit so we ;; have to use this kludge to avoid truncating the packet. (##sys#with-print-length-limit #f (lambda () (debug-print (fmt #f "WRITE " (wrt packet))) (display packet out))) (flush-output out)))
これパケットの送出ルーチンだな。送りたいメッセージと(TCPの)ポートを受け取り、頭に 16進6桁のパケット長データを付け加えて送ってるんだな。
;; Tail-recursive loop to read commands from SWANK socket and dispatch them. ;; Several calls to this may be active at once e.g. when using the debugger. (define (swank-event-loop in out) (let* ((length (read in)) (request (read in))) (cond ((or (eof-object? length) (eof-object? request)) (void)) (else (debug-print (fmt #f "READ " (wrt length) (wrt request))) (case (car request) ((:emacs-rex) (begin (apply swank-emacs-rex in out (swank-cleanup (cdr request))) (swank-event-loop in out))) ((:emacs-return-string) (cadddr request)))))))
こちらは、パケットを読み込んで処理する部分か。最初の想像では、scheme側のreplとやり取り してるかと思ったら違うのね。単にヘッダー部分を落としておいて、それをswank-cleanupで lisp語からscheme語に変換して(例 nil -> #f)、apply使って、処理してる。
で、最終的にS式を処理してるのが下記の手続きだ。
;; Evaluate an S-expression and returns a pair (condition . trace) if an ;; exception is raised or (#t . value) on success. (define (swank-eval-or-condition sexp) (call-with-current-continuation (lambda (skip) (with-exception-handler (lambda (exn) (let ((chain (or ((condition-property-accessor 'exn 'call-chain #f) exn) (get-call-chain)))) (set! *recent-call-chain* (reverse chain)) (skip (cons exn chain)))) (lambda () (cons #t (eval sexp)))))))
出、出たな! call-with-current-continuation。haskellの喪等とschemeのcall/ccは業界の鬼門 とされるからなあ。早速、あんちょこ見ましょ。
なんでも継続 とか Scheme:使いたい人のための継続入門 とか、、、まだまだ有って Scheme/継続の種類と利用例 とか 継続と継続渡しスタイルが参考に なるかな。嗚呼、まだ有っんで継続します。 (継続の使い方)、 継続 。
現実に戻って、他にもcall/ccが無いかと思って探してみたら、サーバーを作ってswank-event-loopを呼び出している あたりにも有ったよ。
;; The top level continuation. By invoking this we can jump out of the ;; debugger and get back to the REPL. (define *swank-top-level* #f)
(lambda () (call-with-values (lambda () (tcp-accept listener)) (lambda (in out) (call/cc (lambda (hop) (set! *swank-top-level* hop))) ;; Whenever we escape from the debugger we'll end up here (let ((orig-handler (current-exception-handler))) (with-exception-handler (lambda (exn) (cond (((condition-predicate 'user-interrupt) exn) (*swank-top-level* (void))) (else (orig-handler exn)))) (lambda () (swank-event-loop in out)))))))
後は膨大にあるswank:XXX 手続き軍だな。ああ、なかなか味な誤変換するわい。 一番お世話になりそうなもの。
;; Evaluate `str' as if enclosed in (begin ...) and return the results. (define (swank:listener-eval str) (call-with-values (lambda () (let ((forms (string->forms str))) (if (not (null? forms)) (eval `(begin ,@forms))))) (lambda results `(:ok (:values ,@(map (lambda (r) (fmt #f (wrt r))) results))))))
Gauche用slime
次はgauche用だな。
ネットワーク関係の手続き中でdispatch-eventが呼ばれるんだな。そこで処理を振り分け ているっぽい。
(define (dispatch-event event) (log-event "dispatch-event: ~s~%" event) (match event ((:emacs-rex params ...) (apply emacs-rex params)) ((:emacs-interrupt thread-id) (interrupt-worker-thread thread-id)) (((or :write-string :debug :debug-condition :debug-activate :debug-return :channel-send :presentation-start :presentation-end :new-package :new-features :ed :%apply :indentation-update :eval :eval-no-wait :background-message :inspect :ping :y-or-n-p :read-string :read-aborted :return) params ...) (write-packet event)) (_ (log-event "Unknown event: ~s~%" event))))
んでもって、emacs-rexの中でエラー処理にガードされながら
(lambda () (write-return `(:ok ,(apply (swank-gauche: (car sexp)) (cdr sexp)))) (set! ok #t))
lambdaで囲ってるけど、これ、外側を包んでいるdynamic-windが単に手続きを3個要求してる んで、無名な手続きって事なんだな。 (参考にどうぞ) ここでもやっぱりapplyで手続きを動的に呼び出してる。 そんじゃ、一つだけ、swank系の処理を見ておくか。
;;;; Evaluation (defslimefun listener-eval (string) (call-with-values (lambda () (eval-repl string)) (lambda ret (set!* /3 /2 *3 *2 /2 /1 *2 *1 /1 (if (null? ret) (values) ret) *1 (if (null? ret) (values) (car ret))) `(:values . ,(map write-to-string/ss ret)))))
本当に大事な所はeval-replだな。後の部分は履歴の処理か。
(define (eval-repl string) (let ((sexp (read-from-string string))) (if (eof-object? sexp) (values) (with-io-repl (lambda () (set!* %3 %2 %2 %1 %1 %0 %0 sexp) (match sexp (('select-module module) ;; select-module is special (eval sexp (user-env (*buffer-package*))) ((global-variable-ref 'swank-gauche 'new-package) module)) (else (eval sexp (user-env (*buffer-package*))))))))))
vim側
余りscheme側に肩入れしてると文句が出そうなんで、今度はvim側のスクリプトも 覗いておく。まずは、swank.pyだな。
def swank_rex(action, cmd, package, thread, data=''): """ Send an :emacs-rex command to SWANK """ global id id = id + 1 key = str(id) actions[key] = swank_action(key, action, data) form = '(:emacs-rex ' + cmd + ' ' + package + ' ' + thread + ' ' + str(id) + ')\n' swank_send(form)
後は、swank_listenっていう巨大なメソッドの中で、ごにょごにょやってる。(:returnを受け取った 場合とか:okを受け取った場合とか。S式のパーサーも含まれているよ)
つらつらとこれを読んでいると、gaucheのslimeがハングした理由が分かった。モジュールなんて メッセージは想定してないのね。まあ、ローカル仕様だからしょうがないか。
んでもって、これらの蛇仕様メソッドは、slimv.vim内からいろいろな方法で呼び出されている。 例を挙げれば
python import vim execute 'pyfile ' . g:swank_path
execute 'python swank_connect("' . g:swank_host . '", ' . g:swank_port . ', "result" )'
call SlimvCommandGetResponse( ':create-repl', 'python swank_create_repl()' )
silent execute 'python get_indent_info("' . func . '")'
vimでもPythonをこき使う
上記で、ちょいとPythonが出てきたんで、おいらもつられてpythonを使ってみる。 その為の先生は、vim上で
:h python
すれば得られる。KaoriYaさんとこのvimには、きっちりと日本語化されたdocが内蔵されてるんで 有り難い。無い人は、 vimdoc-jaを参照。
:[range]py[thon] {stmt} Pythonのステートメント{stmt}を実行します。 :[range]py[thon] << {endmarker} {script} {endmarker} Pythonのスクリプト{script}を実行します。 Note: このコマンドはPython用の機能を含めてコンパイルさ れていないときは機能しません。エラーを抑制するには |script-here|を参照してください。 {endmarker}の前に空白を置かないでください。"<<"の後に{endmarker}省略した時は |:append|や|:insert|のように'.'が使われます。 この形の|:python|コマンドはVimスクリプトにPythonコードを埋め込むのに特に便利で す。 例: function! IcecreamInitialize() python << EOF class StrawberryIcecreame: def __call__(self): print 'EAT ME' EOF endfunction Note: Pythonはインデントに関して非常に繊細です。"class"の行と"EOF"の行はまった くインデントしないでください。
これが基本の基ね。ご丁寧にPython独自の注意書きまでしてあって、どこまで親切なんだよう。 それに引き換え、elispときたら、おめーら好きに書いていいぜって態度だもんなあ。まあ、 elisp書くよりは楽と言えば楽なんでいいけど。
でもって、つらつらと眺めて行くと、vimの画面を配列のようにアクセス出来る事が分かった。 配列だから、スライスも簡単に扱えそうで面白いな。
下記は超簡単な例
import vim b = vim.current.buffer print(b) b.append("Insert from Python") b.append( b[0] )
こんな簡単なtest.pyを用意して、
:pyfile test.py
すれば、今のバッファーの名前をステータス行に表示して、バッファーの最後に蛇からの目印を 追加し、更に1行目をコピーしてきて追加出来る。蛇を使うのやだって人なら、rubyでも同じ事が簡単に出来るよ。
所で、インポートしてるvimって何処に入ってるのかな? vimの中に組み込まれているのかな。 探してみたけど所在不明でした。
おまけ
これ、emacsが無ければやっていけない。でも時々vimの気分にも浸りたいという人向けの ものです。きっと指使いが混乱するぞ。ヒヒヒ。