r7rs-swank
前回はLemなんて言うemacs似なeditorを知ったものだから、少し戯れてしまった。そこで、directory-modeを見つけてしまったんだけど、いかにも長ったらしいモードだ。きっと何かのキーにバインドされてるだろうな、なんて考えながら、哲学の道(誰も通らない農道だけど)を散歩してたんだ。
閃いたよ。dirを指定してlemを起動しちゃえばいいんでないかい。石頭のeditorだとバイナリーファイルだけどどうするって聞いてくるだろうね。emacsだとdiredが起動するし、lemだとdirecory-modeが起動した。ユーザーの意を汲んでくれているんだね。
この際、lemでどんなキーバインドが登録されてるか確認しとく。 主な場所は、lib/coreの中。
(base) sakae@debian:core$ grep -h global-keymap * : (define-key *global-keymap* "C-x C-f" 'find-file) (define-key *global-keymap* "C-x C-r" 'read-file) (define-key *global-keymap* "C-x C-s" 'save-buffer) (define-key *global-keymap* "C-x ?" 'describe-key) ;; (define-key *global-keymap* "C-h m" 'describe-mode)
あれま? モードの説明はどうしちゃったんでしょうか。
探した限りでは、ホイホイとファイルを見て行くモードは無いようだ。C-x C-r で開くの面倒っぽい。通な人は、開いた限りは修正するもんだと、刷り込まれているんだろうね。
上で偉そうにemacsでdirを指定して開くとdiredモードに成るなんて書いたけど、今回が初体験です。通常は、emacsを起動して、そこからC-x d して、~/src/hoge/*.h とかと入力して、ヘッダーファイルだけを対象とかに指定してます。こうしないと目移りしちゃうからね。
後は、スペース/バックスペースで、見たいファイルに照準を合わせ、v(iew)って流れ。viewモードで閲覧中もスペース/バックスペースでページを前後に移動。身飽きたらqで元のファイル選択画面に戻る。キー操作が簡単で気に入っています。
是非この機能を実現してください。本当はツイターとかSNSで作者に希望が届けば良いのでしょうが、生憎オイラーは両方共やってません。>誰か代理で呟いてください。
ええ、勿論lemで、上下矢印、PageUp/Downで、大方の機能は実現出来る事知ってます。けど、矢印キーとか使うのは嫌いなんです。ホームポジションから指が離れてしまいますからね。
r7rs-swank
r7rs-swankには、それぞれのschemeでサーバーに成るためのshellスクリプトが用意されてる。こやつを起動して、まずはエラーにならない事が求められる。
で、OpenBSDでchickenを試してみると、r7rs不足と言われたよ。まだr7rsは特殊な世界なんだな。 chicken-install -s r7rs して、r7rsの世界に没入。 起動時の状況。
ob$ ps -ao command COMMAND csi -R r7rs chicken-swank.scm -b -e (start-swank) (chicken-csi) sh chicken.sh
OpenBSDにはlsofが無いので、fstatで代用。
ob$ fstat -p 57479 USER CMD PID FD MOUNT INUM MODE R/W SZ|DV sakae chicken-csi 57479 text / 1411734 -rwxr-xr-x r 246336 sakae chicken-csi 57479 wd /tmp 218688 drwxr-xr-x r 1024 sakae chicken-csi 57479 0 / 676081 crw--w---- rw ttyp1 sakae chicken-csi 57479 1 / 676081 crw--w---- rw ttyp1 sakae chicken-csi 57479 2 / 676081 crw--w---- rw ttyp1 sakae chicken-csi 57479 3* internet stream tcp 0x0 *:4005
4005で待ち受け受信の体制になってる。ならば、emacs上で M-x slime-connectすれば、繋がるはず。
Connecting to Swank on port 4005.. error in process filter: slime-dispatch-event: slime-dcase failed: (|:return| (\ |:ok| (|:pid| 123 |:style| |:spawn| |:encoding| (|:coding-systems| ("utf-8-unix\ ")) |:lisp-implementation| (|:type| "Scheme" |:name| "chicken-scheme" |:version\ | 123 |:program| "/usr/bin/scheme") |:machine| (|:instance| "host" |:type| "X86\ -64") |:features| (|:swank|) |:modules| ("SWANK-ARGLISTS" "SWANK-REPL" "SWANK-P\ RESENTATIONS") |:package| (|:name| "(user)" |:prompt| "(user)") |:version| "2.2\ 4")) 1)
見事にエラーなんで、記念撮影 *Messages*をインスタグラムならぬ当ページへ貼り付け。 起動側のコードにdebug?するかいフラグが有ったのでOnと言うかTeaした。
common/base.scm (define debug? #t)
ob$ sh chicken.sh swank listening on port 4005 from slime> (:emacs-rex (swank:connection-info) "COMMON-LISP-USER" t 1) to slime< (|:return| (|:ok| (|:pid| 123 |:style| |:spawn| |:encoding| (|:coding-systems| ("utf-8-unix")) |:lisp-implementation| (|:type| "Scheme" |:name| "chicken-scheme" |:version| 123 |:program| "/usr/bin/scheme") |:machine| (|:instance| "host" |:type| "X86-64") |:features| (|:swank|) |:modules| ("SWANK-ARGLISTS" "SWANK-REPL" "SWANK-PRESENTATIONS") |:package| (|:name| "(user)" |:prompt| "(user)") |:version| "2.24")) 1)
やり取りは正常っぽく見えるぞ。
Error: (utf8->string) bad argument type - not a structure of the required type #!eof u8vector Call history: <eval> [write-packet] (string-pad-left hex-len 6 #\0) <eval> [string-pad-left] (string-length string) <eval> [string-pad-left] (max 0 (- size s-len)) : <eval> [read-packet] (utf8->string (read-bytevector 6 port)) <eval> [read-packet] (read-bytevector 6 port) <--
でも、debugを外すと、こんなのが出てくるからなあ。https://www.call-cc.org/には、相変わらず繋がらない。call/ccが継続してないじゃん。
kawa
取り合えずにわとりは置いておいて、新種のschemeであるkawaを試してみるか。
javaの上に構築されてるschemeなんで、
java -jar kawa.jar
で起動出来るはずなんだけど、何故か/usr/local/bin/kawaはバイナリーファイルになってる。そして普通に起動すると
java -Dkawa.command.line=kawa kawa.repl --connect 6160
4005で待っていないのが、詐欺っぽいな。なんせ日本では、kawa-sagi なんて鳥もいますから。swankするかお試しする。
ob$ sh kawa.sh /tmp/r7rs-swank/common/handlers.scm:358:29: '::' must follow parameter name /tmp/r7rs-swank/common/handlers.scm:358:29: misformed formals in lambda /tmp/r7rs-swank/common/handlers.scm:361:29: '::' must follow parameter name /tmp/r7rs-swank/common/handlers.scm:361:29: misformed formals in lambda /tmp/r7rs-swank/specific/kawa.scm:22:29: warning - no known slot 'accept' in jav a.lang.Object /tmp/r7rs-swank/specific/kawa.scm:23:17: warning - no known slot 'getInputStream' in java.lang.Object /tmp/r7rs-swank/specific/kawa.scm:23:73: warning - no known slot 'getOutputStream' in java.lang.Object :
見事に離陸失敗、残念至極。
slime/contrib/swank-kawa.scmなコードが綺麗で、参考になるな。実際に試すにはJavaとかを良く知らないと駄目ぽいけど。
at racket on CentOS
舞台をCentOSに移して、各種のschemeでやってみよう。何せコレクションしていますから。一番期待の持てそうなracketです。こやつは、黒い窓恐いの若者向けにDrRacketなんてのを手掛けてますからね。
(base) [sakae@c8 r7rs-swank-Gauche-custom]$ sh racket.sh standard-module-name-resolver: collection not found for module path: r7rs/lang/reader collection: "r7rs/lang" in collection directories: /home/sakae/.racket/7.4/collects /usr/local/racket/collects ... [160 additional linked and package directories] context...: show-collection-err :
で、早速エラーになりました。どうやらr7rsは先端過ぎて、通常のインストールでは関係者が同居してないようです。
(base) [sakae@c8 r7rs-swank-Gauche-custom]$ raco pkg install r7rs Resolving "r7rs" via https://download.racket-lang.org/releases/7.4/catalog/ Resolving "r7rs" via https://pkgs.racket-lang.org Downloading repository git://github.com/lexi-lambda/racket-r7rs?path=r7rs : raco setup: 1 making: <pkgs>/r7rs-lib raco setup: 1 making: <pkgs>/r7rs-lib/lang raco setup: 1 making: <pkgs>/r7rs-lib/lang/private raco setup: 1 making: <pkgs>/r7rs-lib/load raco setup: 1 making: <pkgs>/r7rs-lib/load/lang raco setup: 1 making: <pkgs>/r7rs-lib/private raco setup: --- creating launchers --- [14:12:52] raco setup: --- installing man pages --- [14:12:52] raco setup: --- building documentation --- [14:12:52] raco setup: --- installing collections --- [14:12:52] raco setup: --- post-installing collections --- [14:12:52]
racoとか言うパッケージマネージャーを使って、入れてあげます。これで、サーバー側は起動しました。
(base) [sakae@c8 ~]$ ps a 2927 pts/2 Sl+ 0:01 racket -I r7rs racket-swank.sld (base) [sakae@c8 ~]$ lsof -p 2927 racket-sw 2927 sakae 5u IPv6 52869 0t0 TCP *:pxc-pin (LISTEN) racket-sw 2927 sakae 7u IPv4 52870 0t0 TCP *:pxc-pin (LISTEN) (base) [sakae@c8 ~]$ grep pxc-pin /etc/services pxc-pin 4005/tcp # pxc-pin pxc-pin 4005/udp # pxc-pin
lsofでポートを確認したら、変なやつが出てきたので、素の番号を調べてみました。slimeのデフォのポート番号だよって、出願しなかったものだから、変な名前で登録されてるではないですか。弱小企業slime。
サーバーを立てておいてから、emacsで M-x slime-connectします。
Scheme Port: 4005 Pid: 123 ; SLIME 2.24 (user)> (version) "7.4" (user)> (define (hoge n) (* n n n)) #<void> (user)> (hoge 5) 125
嘘のPidと言うか、問い合わせた時の返答をそのまま表示してます。一応replとしては動いているようです。(miniBufferにグダグダとエラーのメッセージが出てきますが)
at gauche
Scheme Port: 4005 Pid: 3434 ; SLIME 2.24 (user)> (gauche-version) "0.9.8"
正しいPidを表示してます。補完も効くし、後はedit-bufferとの連携ですかね。宜しくお願いします。
swank.rb
slime/contribの中に面白い物を発見。
debian:tmp$ ruby -r ./swank -e swank Listening on ["AF_INET6", 4005, "::1", "::1"] dispatch: [:":emacs-rex", [:"swank:connection-info"], "COMMON-LISP-USER", :t, 1] dispatch: [:":emacs-rex", [:"swank:swank-require", [:quote, [:"swank-trace-dialog", :"swank-package-fu", :"swank-presentations", :"swank-macrostep", :"swank-fuzzy", :"swank-fancy-inspector", :"swank-c-p-c", :"swank-arglists", :"swank-repl"]]], "COMMON-LISP-USER", :t, 2]
M-x slime-connectすると、
Undefined function: swank:swank-require [RuntimeError] Restarts: 0: [Quit] SLIME top-level. Backtrace: 0: /tmp/swank.rb:80:in `emacs_rex' 1: /tmp/swank.rb:64:in `dispatch' 2: /tmp/swank.rb:49:in `block in main_loop' 3: /tmp/swank.rb:48:in `catch' 4: /tmp/swank.rb:48:in `main_loop' 5: /tmp/swank.rb:42:in `serve' 6: /tmp/swank.rb:29:in `accept_connections' 7: /tmp/swank.rb:14:in `swank' 8: -e:1:in `<main>'
哀れな事に、未実装とな。でも、まあ通信は出来ているって事で誉めてあげましょう。
この他、swank-mlworks.smlなんて言うSML系のやつもいた。骨休めに眺めてみると面白いかも。
r7rs-swank-gauche
r7rs-gaucheなサーバーを起動しておいて、emacsからconnectしpromptが出てくるまでのログ。
swank listening on port 4005 from slime> (:emacs-rex (swank:connection-info) "COMMON-LISP-USER" t 1) to slime< (:return (:ok (:pid 2812 :style :spawn :encoding (:coding-systems ("utf-8-unix")) :lisp-implementation (:type "Scheme" :name "gauche-scheme" :version "0.9.8" :program "gosh") :machine (:instance "host" :type "X86-64") :features (:swank) :modules ("SWANK-ARGLISTS" "SWANK-REPL" "SWANK-PRESENTATIONS") :package (:name "(user)" :prompt "(user)") :version "2.24")) 1) from slime> (:emacs-rex (swank:swank-require (quote (swank-trace-dialog swank-package-fu swank-presentations swank-macrostep swank-fuzzy swank-fancy-inspector swank-c-p-c swank-arglists swank-repl))) "COMMON-LISP-USER" t 2) to slime< (:return (:ok ()) 2) from slime> (:emacs-rex (swank:init-presentations) "COMMON-LISP-USER" t 3) to slime< (:return (:ok ()) 3) from slime> (:emacs-rex (swank-repl:create-repl nil :coding-system "utf-8-unix") "COMMON-LISP-USER" t 4) to slime< (:return (:ok ("(user)" "(user)")) 4)
replから、(cdr '(a b c)) を評価してみた。
from slime> (:emacs-rex (swank:autodoc (quote ("cd" swank::%cursor-marker%)) :pr int-right-margin 80) "(user)" :repl-thread 5) to slime< (:return (:abort "unbound variable: cd: ") 5) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" swank::%cursor-marker%)) :p rint-right-margin 80) "(user)" :repl-thread 6) to slime< (:return (:ok ("(===> cdr <=== obj)" t)) 6) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" "" swank::%cursor-marker%)) :print-right-margin 80) "(user)" :repl-thread 7) to slime< (:return (:ok ("(cdr ===> obj <===)" t)) 7) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" "'" swank::%cursor-marker%$) :print-right-margin 80) "(user)" :repl-thread 8) to slime< (:return (:ok ("(cdr ===> obj <===)" t)) 8) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" ("" swank::%cursor-marker%$)) :print-right-margin 80) "(user)" :repl-thread 9) to slime< (:return (:abort "unbound variable: ||: ") 9) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" ("a" "" swank::%cursor-mar$er%))) :print-right-margin 80) "(user)" :repl-thread 10) to slime< (:return (:abort "unbound variable: a: ") 10) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" ("a" "b" "" swank::%cursor$marker%))) :print-right-margin 80) "(user)" :repl-thread 11) to slime< (:return (:abort "unbound variable: a: ") 11) from slime> (:emacs-rex (swank:autodoc (quote ("cdr" ("a" "b" "c" swank::%curso$-marker%))) :print-right-margin 80) "(user)" :repl-thread 12) to slime< (:return (:abort "unbound variable: a: ") 12) from slime> (:emacs-rex (swank-repl:listener-eval "(cdr '(a b c)) ") "(user)" :repl-thread 13) to slime< (:write-string "") to slime< (:presentation-start 1 :repl-result) to slime< (:write-string "(b c)" :repl-result) to slime< (:presentation-end 1 :repl-result) to slime< (:write-string "\n" :repl-result) to slime< (:return (:ok ()) 13)
(cdr まで入力した所で、関数名が確定し、miniBufferには、(cdr obj) って、案内が出てきた。
from slime> (:emacs-rex (swank:completions "cad" (quote "(user)")) "(user)" :repl-thread 36) to slime< (:return (:ok (("caddar" "cadar" "cadddr" "cadadr" "caddr" "cadr" "cadaar" "cadaar" "cadddr" "caddar" "cadadr" "caddr" "cadar") "cad")) 36) from slime> (:emacs-rex (swank:autodoc (quote ("cad" swank::%cursor-marker%)) :print-right-margin 80) "(user)" :repl-thread 37) to slime< (:return (:abort "unbound variable: cad: ") 37)
cadまで入力してTABを叩くと補完の問い合わせがgaucheに行く。それに答えて候補リストが返るとな。問い37は、候補をその場で選ぶような仕様だろ(lemでの挙動)。実験してるのは、普通のemacsなんで、エラーが返っているんだな。
from slime> (:emacs-rex (swank:autodoc (quote ("cadr" swank::%cursor-marker%)) :print-right-margin 80) "(user)" :repl-thread 38) to slime< (:return (:ok ("(===> cadr <=== obj)" t)) 38) from slime> (:emacs-rex (swank:autodoc (quote ("cadr" "" swank::%cursor-marker%)) :print-right-margin 80) "(user)" :repl-thread 39) to slime< (:return (:ok ("(cadr ===> obj <===)" t)) 39) from slime> (:emacs-rex (swank:autodoc (quote ("cadr" "'" swank::%cursor-marker%)) :print-right-margin 80) "(user)" :repl-thread 40) to slime< (:return (:ok ("(cadr ===> obj <===)" t)) 40) : from slime> (:emacs-rex (swank:autodoc (quote ("cadr" ("a" "b" "c" "" swank::%cursor-marker%))) :print-right-margin 80) "(user)" :repl-thread 44) to slime< (:return (:abort "unbound variable: a: ") 44)
cadrって確定させたので、autodocの説明要求ね。 手に取るように、やり取りが確認出来て面白い。
handler
r7rs-swankは、emacsからアクセスされるサーバーだ。クライアントからは、色々な要求が飛んでくる。その要求の一つ一つに対して、どんな返答をしたらいいかが、handlers.scmに定義されてる。
冒頭にはマクロが定義されてて、ハンドラーの定義が間違いなく書けるように配慮されてた。
(base) [sakae@c8 common]$ grep define-slime-handler handlers.scm (define-syntax define-slime-handler ((define-slime-handler (name . params) body0 body1 ...) (define-slime-handler (:emacs-rex sexp env-name thread id) (define-slime-handler (swank:connection-info) (define-slime-handler (swank:swank-require packages) : (define-slime-handler (swank::menu-choices-for-presentation-id id) (define-slime-handler (swank::describe-to-string object) (define-slime-handler (swank:find-definitions-for-emacs name)
幾つあるかと数えたら、50個を超えてた。slimeって奴は、本当に色々と要求を出すものだ。
代表的なやつを見てみる。要求を受領して実行してね。
(define-slime-handler (:emacs-rex sexp env-name thread id) (call-with-current-continuation (lambda (exit) (parameterize ((param:abort (lambda (message) (exit `(:return (:abort ,message) ,id)))) (param:environment ($environment env-name)) (param:current-id id)) `(:return (:ok ,(process-form sexp env-name)) ,id)))))
おっ、ここでも有名なcall/ccが出てきた。parameterizeを使って、評価を失敗したら、エラーで脱出(継続)するように設定。そうしておいてから要求の有ったsexpを評価。
実際の評価は、base.scm/process-formに有った。
(define (process-form form env-name) (let ((key (car form))) (let ((h (find-handler key))) (if h (with-exception-handler (lambda (condition) ($handle-condition condition) (swank/abort ($error-description condition))) (lambda () (apply h (cdr form)))) `(:return (:abort "no handler found") nil)))))
gauche 0.9.9
おめでとうございます。オイラーも早速更新。
gauche-config --reconfigure | sh make : gosh -V Gauche scheme shell, version 0.9.9 [utf-8,pthreads], i686-pc-linux-gnu (version "0.9.9") (command "gosh") (scheme-id gauche) (language scheme r5rs r7rs) (website "https://practical-scheme.net/gauche") (platform "i686-pc-linux-gnu")
これがr7rs流の自己紹介?
debian:r7rs-swank-Gauche-custom$ sh gauche.sh gosh: "ERROR": uninitialized variable: symbol->string : gosh> (symbol->string 'cons) "cons"
あれ?
debian:r7rs-swank-Gauche-custom$ git pull remote: Enumerating objects: 13, done. remote: Counting objects: 100% (13/13), done. remote: Compressing objects: 100% (2/2), done. remote: Total 7 (delta 5), reused 7 (delta 5), pack-reused 0 Unpacking objects: 100% (7/7), done. From https://github.com/Hamayama/r7rs-swank-Gauche-custom 8d7e3d1..3fe0042 master -> origin/master Updating 8d7e3d1..3fe0042 Fast-forward README.md | 3 ++- gauche-main.scm | 7 ++++++- gauche-swank.sld | 16 ++++------------ specific/gauche.scm | 7 ++++--- 4 files changed, 16 insertions(+), 17 deletions(-) debian:r7rs-swank-Gauche-custom$ sh gauche.sh
もう追従されてる。仕事早いな。