geiser (2)

VMWARE Playerが新しい版になったのでアップデートしてって案内が出てくるようになった。 が、喜びいさんでokをクリックすると、DLに失敗しましたとなる。

この案内は1日に一回しか表示されなくて、数日に渡りチャレンジしたが、さっぱりDLをする兆しが無い。もう来年まで待てって事かな。それとも、(金を払ってる)ライセンス所持者が優先されるのかな?

1週間ぐらい経ってから試してみたら、やっとこさアップデート出来た。で、いざ新しいやつで起動すると、オーサライズのサービスが動いていませんので、仮想マシンを起動出来ませんでしたですって。手動でオーサライズサービスを起動してから試してみてください。

そんな事を言われてもねぇ。誰か経験者はいらっしゃいませんか?

http://yvernis.blog.fc2.com/blog-entry-146.html

https://blog.oyasu.info/2018/07/07/6564/

こんなのが出てきた。Windows10のシステムドライバーが古くて、新しいVMWAREには対応できないみたいだ。

一番簡単な解決法、vmwareのインストーラーを取ってきて、インストールを始める。その初期の段階で、インストールするか修復するか選択出来るようになってる。迷わず修復インストールを選んだよ。

そしたら、件のドライバーも2016年製から、今月製のものに改められた。これで、無事に VMWARE 15.5.1が動き出した。よかったよかった。

ダウンロード VMware Workstation Player

gambit on Debian

debianが提供するgambitをemacs上のgeiserから使おうとすると、

Geiser requires gambit version v4.9.3 but detected v4.8.8

古すぎると文句を言われた。そこで、最新のソースから入れてあげる事にした。 作成時の注意が一つある。configする時、 --enable-single-host を付けてくれってお願いだ。忘れると、作成にめっぽう時間がかかるらしい。

おかげで、約5分で作成完了。何も気にしないで作ったものだから、 いや、正しい作り方が出てたぞ。 geiser ちゃんとやれよ。

/usr/local/Gambit/bin
debian:bin$ ls
gambcomp-C*       gambcomp-ruby*  gsi-script@             six@
gambcomp-java*    gambdoc*        scheme-ieee-1178-1990@  six-script@
gambcomp-js*      gsc*            scheme-r4rs@
gambcomp-php*     gsc-script@     scheme-r5rs@
gambcomp-python*  gsi*            scheme-srfi-0@

こんな、今風の場所に設置された。それはいいんだけど、gambcomp-rubyとかは何者?

debian:bin$ cat gambcomp-ruby
   :
  "exe")

    echo "#! /usr/bin/env ruby" > "${BUILD_EXE_OUTPUT_FILENAME}"
    cat ${BUILD_EXE_INPUT_FILENAMES} "${GAMBITDIR_LIB}/_gambit.rb" >> "${BUILD_EXE_OUTPUT_FILENAME}"
    chmod +x "${BUILD_EXE_OUTPUT_FILENAME}"
    ;;

  *)
    echo "gambcomp-ruby unknown operation \"$1\""
    exit 1
    ;;

esac

ruby用のスクリプトを作成してるようだけど、これがgambitとどう絡んでくるの? 予想するに、schemeで書いたコードが、_gambit.rb に出力されて、それと、入力ファイルを組み合わせて、rubyのコードを完成させる。

これが正しいなら、schemeで一度書けば、有名な言語とミックスして使えるようになるバラ色の世界が待ってますとな。

edebugの実習

まだ試していなかったので、geiser-repl.elの下記関数の所にカーソルを持って行って、C-u C-M-x して対象を登録。

次に、C-c C-z でgeiserを起動。引っかかってきたんで、SPCで進める。

(defun geiser-repl--start-scheme (impl address prompt)
  (setq comint-prompt-regexp prompt)
  (let* ((name (geiser-repl--repl-name impl))
         (buff (current-buffer))
         (args (cond ((consp address) (list address))
                     ((stringp address) '(()))
                     (t `(,(geiser-repl--binary impl)
                          nil
=>                        ,@(geiser-repl--arglist impl))))))

geiser-repl--arglistの評価結果。なんだか見た事が有るなあ。

Result: ("-:d-" "/home/sakae/.emacs.d/elpa/geiser-20191023.424/scheme/gambit/geiser/gambit.scm" "-")

更に進めて

              (goto-char (point-max))
              (set-marker (process-mark proc) (point)))
=>        (apply 'make-comint-in-buffer `(,name ,buff ,@args)))
      (error (insert "Unable to start REPL:\n"
                     (error-message-string err)
                     "\n")
             (error "Couldn't start Geiser: %s" err)))
    (geiser-repl--wait-for-prompt geiser-repl-startup-time)))

このapplyが評価されると、gsiが起動してくる。 中々便利だな。 edebugの呼び出しを中止するには、該当のソースを再ロードする(ちょっとローテクっぽいけど、load-file) d でバックトレースを確認。?もお決まりね。

scheme bufferでS式を評価(一文字入力の度に呼ばれるなあ)

geiser-eval.el

(defun geiser-eval--send/wait (code &optional timeout buffer)
=>(setq geiser-eval--sync-retort nil)
    :

この時のバックトレースを edebugのdコマンドで得る。

  geiser-eval--send/wait((:eval (:scm "aa")))
  geiser-debug--send-region(nil 33 35 nil nil nil)
  geiser-eval-region(33 35 nil t nil)
  geiser-eval-last-sexp(nil)
  funcall-interactively(geiser-eval-last-sexp nil)
  call-interactively(geiser-eval-last-sexp nil nil)
  command-execute(geiser-eval-last-sexp)

geiser HACKING

gitから落としてきたgeiserにハックの文書が入っていた。elpaから入れちゃうと、こういうの省略されちゃうからね。協力者のリストも載ってた。日本人とおぼしき名前もでてたぞ。 それはそうと、HACKINGの文書の一部

To see what elisp functions one needs to implement, just execute the
command `M-x geiser-implementation-help` inside emacs with a recent
version of geiser installed. And then take a look at, say,
geiser-guile.el or geiser-racket.el for examples of how those
functions are implemented for concrete schemes (those are the most
featureful implementations we have, so perhaps it's easier to begin
with something like geiser-chicken.el or geiser-chibi.el).

ふむ、実装のためのHelpが有るとな。それより、コードを読むのが一番。guileとraketが、いいよ(確かに機能が充実)。お手軽なchickenとかchibi(チビって、日本だと言葉狩りにあって、差別用語に認定されてるのかな)とかも有るよとな。要するに好きなの見ろ。誰も止めないから。

そして、そに下の段落では、実装するschemeによって、どうしても実現出来ない機能があるだろうかけど、そんなの気にするな、とも。HACKER精神が発露された激励文ですなあ。

Geiser: supporting new Scheme implementations.

Use `define-geiser-implementation' to define new implementations

  (define-geiser-implementation NAME &rest METHODS)

Defines a new supported Scheme implementation.
NAME can be either an unquoted symbol naming the implementation,
or a two-element list (NAME PARENT), with PARENT naming another
registered implementation from which to borrow methods not
defined in METHODS.

After NAME come the methods, each one a two element list of the
form (METHOD-NAME FUN-OR-VAR), where METHOD-NAME is one of the
needed methods (for a list, execute ‘geiser-implementation-help’)
and a value, variable name or function name implementing it.
Omitted method names will return nil to their callers.

Here’s how a typical call to this macro looks like:
  :

helpを引いてみた。個別に実装するには、それぞれのコードを書いて、登録しておけとな。 それぞれの変数とか関数の簡単な説明と、どこで利用されてるかの説明が続いていたぞ。

そうか、geiserとしての核の機能が提供され、それを各schemeの特性に合わせてカスタマイズ してくんだな。そのコードが、各生のschemeを制御するとな。

なんだかOSの抽象化されたファイルシステムの扱いみたいだな。OSに相当するのが、geiserの核の部分。CDとかNTFSとかの個別のファイルシステムに相当するのが、各scheme用(gambitとかracketとか)のelispコード。その下層は、それぞれのハードになるけど、相当するのは、schemeのバイナリーとな。

run-scheme (ru-s)

普通のschemeの起動はどうなっている? FreeBSD 12.1 の新たなOSで試す。一時、gaucheのportsのメンテナが居なくて放置されてたけど、どなたかが引き受けてくれててpkgになってたので、自前で入れるのはショートカット。

(setq scheme-program-name "/usr/local/bin/gosh -i")
(require 'cmuscheme)

取り合えずの設定。そして C-h f run-scheme したら xscheme.el に案内された。

スクラッチパッドで、(run-scheme scheme-program-name)を評価

xscheme-start-process で待ち構える。 追って行ったら、start-processが怪しそうなので、この関数に d-o-e 。edebugは使い易くていいんだけど、思わぬ副作用が有って、あれれとなる事がある。ある程度追い詰めたら、debug-on-entry な奴に切り替えると良い。

Debugger entered--entering a function:
* apply(make-process (:name "scheme" :buffer #<buffer *scheme*> :command ("/usr$
* #f(compiled-function (name buffer program &rest program-args) "Start a progra$
* apply(#f(compiled-function (name buffer program &rest program-args) "Start a $
* start-process("scheme" #<buffer *scheme*> "/usr/local/bin/gosh" "-i")
   :

start-process は、subr.elに有った。dコマンドで更に潜って行くと、プロセス作成の最下層に達した。

38.4 Creating an Asynchronous Process

geiser start

geiserからお目当てのschemeとどうやって接続してるのか? 今までの調査では、TCPで接続、Unix-domainを使う接続と2種類がソース上で目についた。きっとUnix-domainを使っているんだろうと、その辺を精査してたけど、さっぱりその片鱗が伺えなかった。

上のgaucheの方法を見て、もしやと思い、start-processを追う事にした。

Debugger entered--entering a function:
* start-process("Chez REPL" #<buffer * Chez REPL *> "scheme" "/home/sakae/.emac$
  apply(start-process "Chez REPL" #<buffer * Chez REPL *> "scheme" "/home/sakae$
  start-file-process("Chez REPL" #<buffer * Chez REPL *> "scheme" "/home/sakae/$
  apply(start-file-process "Chez REPL" #<buffer * Chez REPL *> "scheme" "/home/$
  comint-exec-1("Chez REPL" #<buffer * Chez REPL *> "scheme" ("/home/sakae/.ema$
  comint-exec(#<buffer * Chez REPL *> "Chez REPL" "scheme" nil ("/home/sakae/.e$
  make-comint-in-buffer("Chez REPL" #<buffer * Chez REPL *> "scheme" nil "/home$
  apply(make-comint-in-buffer ("Chez REPL" #<buffer * Chez REPL *> "scheme" nil$
  geiser-repl--start-scheme(chez nil "\\(> \\)")
  geiser-repl--start-repl(chez nil)
  run-geiser(chez)
  switch-to-geiser(nil nil #<buffer zz.ss>)
   :

見事に引っかかってきましたなあ。第三の方法で接続されてたのね。しかも入り口が非常に分かりづらい。事情を知ってる人しかたどり着けないよ。

run-python

気をよくしたオイラーは、rubyではどうよと調べるもさぽーとしてない。それではと思って、run-pythonだな。良くも悪くも世界標準だからね。

Debugger entered--entering a function:
* start-process("Python" #<buffer *Python*> "python" "-i")
  apply(start-process "Python" #<buffer *Python*> "python" "-i")
  start-file-process("Python" #<buffer *Python*> "python" "-i")
  apply(start-file-process "Python" #<buffer *Python*> "python" "-i")
  comint-exec-1("Python" #<buffer *Python*> "python" ("-i"))
  comint-exec(#<buffer *Python*> "Python" "python" nil ("-i"))
  make-comint-in-buffer("Python" "*Python*" "python" nil "-i")
  apply(make-comint-in-buffer "Python" "*Python*" "python" nil "-i")
  python-shell-make-comint("python -i" "Python" t)
  run-python("python -i" nil t)
  funcall-interactively(run-python "python -i" nil t)
  call-interactively(run-python record nil)
  command-execute(run-python record)
  execute-extended-command(nil "run-python" "run-pyt")
  funcall-interactively(execute-extended-command nil "run-python" "run-pyt")
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)

これが全貌。python.el から comint.elに入って、そこでごにゃごにゃやってる。ここら辺が肝のなる予感。

emacsからipython(via mlterm)を立ち上げる

Comint: Writing your own Command Interpreter

(comint-run "irb")

これを評価するとirbが起動する。関数にしておけば、手軽に使えるな。< 何をやってるのやら。

M-x list-processで、emacsから起動したプロセスの状態を確認出来るとな。geiserから起動したchezの状態。

Process [v]     PID     Status  Buffer          TTY          Command
Chez REPL       5953    run     * Chez REPL *   /dev/pts/3   scheme /home/sakae$

そしてスクラッチパッドから起動

(start-process "gauche" "mygosh" "/usr/local/bin/gosh" "-i")
(start-process "chez" "mychez" "scheme")

こんなプロセス状態になった。

Process [v]     PID     Status  Buffer          TTY          Command
chez            4852    run     mychez          /dev/pts/5   scheme
gauche          4703    run     mygosh          /dev/pts/4   /usr/local/bin/gos$

但し、bufferでそれぞれのschemeとは交信出来ないみたいだ。ちゃんと手順を踏まないとだめなんだろうね。

emacs src

make_processを分解するとcreate_processとかが出てくる。 但しcreateの方は、emacs上のdebuggerも知らないようで、引っかける事は出来ない。

DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0,
       doc: /* Start a program in a subprocess.  Return the process object for it.

      create_process (proc, new_argv, current_dir);
    }

ってな事で、最後の方で呼び出されていた。毎度思うんだけど、emacsのソースは読まない方が幸せであります。

slime ???

list-processなんて素敵な関数を知ったものだから、slimeでsbclを起動して調べてみた。

Process [v]     PID     Status  Buffer          TTY          Command
SLIME Lisp      --      open     *cl-connect... --           (network connectio$
inferior-lisp   10462   run     *inferior-lisp* --           sbcl

SLIME Lispってプロセスのコマンド表示が途中で切れちゃってるけど、ちゃんと表示させると (network connection to localhost) のようになってた。emacsが嘘をついていないか、別の角度から確認。

(base) sakae@debian:~$ ss -t
State    Recv-Q    Send-Q       Local Address:Port        Peer Address:Port
ESTAB    0         0                127.0.0.1:44164          127.0.0.1:34373
ESTAB    0         0                127.0.0.1:34373          127.0.0.1:44164
ESTAB    0         0                10.0.2.15:ssh             10.0.2.2:51797

うん、嘘はなさそう。更にLinux特有のコマンドでも確認。sbclのPIDが知れているので、詳細を調べてみる。

(base) sakae@debian:~$ lsof -p 10462
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
 :
sbcl    10462 sakae    0r  FIFO   0,12      0t0   32047 pipe
sbcl    10462 sakae    1w  FIFO   0,12      0t0   32048 pipe
sbcl    10462 sakae    2w  FIFO   0,12      0t0   32048 pipe
sbcl    10462 sakae    4u  IPv4  32050      0t0     TCP localhost:34373->localhost:44164 (ESTABLISHED)

sbclはemacsとネットワーク接続されてるのが確認出来た。そこまではいいんだけど、sbclの標準入出力の先がパイプ接続されてる。対抗するemacs側も調べてみると、

(base) sakae@debian:~$ lsof -p 10458
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
 :
emacs   10458 sakae    7u     IPv4  32057      0t0     TCP localhost:44164->localhost:34373 (ESTABLISHED)
emacs   10458 sakae    8w     FIFO   0,12      0t0   32047 pipe
emacs   10458 sakae    9r     FIFO   0,12      0t0   32048 pipe

ふむ、pipeは3本施設されていないね。

(base) sakae@debian:~$ ps awxl
F   UID   PID  PPID PRI  NI    VSZ   RSS WCHAN  STAT TTY        TIME COMMAND
    :
0  1000 10458  1163  20   0 257272 45080 core_s Sl+  pts/1      0:01 emacs
0  1000 10462 10458  20   0 1389932 87860 x64_sy Ssl ?          0:00 /usr/local/bin/sbcl

親子関係が確認出来た。そしてsbclの端末が ? になってるって事は、外界からちょっかいを出す事は出来ないって事だ。じゃ、なんでパイプ接続なんてしてるの? 子離れ出来ないemacsが子のsbclを世に送り出す時に、保険をかけているのではなかろうか?

先に出てきたlist-processのバッファーで、*inferior-lisp* の所がリンクになってるんで、そこをつついてみる。

* WARNING:
   redefining THREAD-FOR-EVALUATION (#<STRUCTURE-CLASS SWANK::MULTITHREADED-CONNECTION>
            #<SB-MOP:EQL-SPECIALIZER :FIND-EXISTING>) in DEFMETHOD
(cdr '(a b c d e))
"Hellow emacs"

じっと目を凝らしてみると、冒頭にsbclのプロンプトの * が見て取れる。って事は、sbclにS式を入力して評価出来るって事じゃろか。cdrなんてクダーらない関数と、親への挨拶文字列を入力してみた。返答は無し。 でも きっと、パイプを通じて親に送られるに違いない。

SBCL  Port: 34373  Pid: 10462
; SLIME 2.24(B C D E)
*
"Hellow emacs"
*
CL-USER> "Hellow SBCL"

"Hellow SBCL"
CL-USER>

こちらは、*slime-repl sbcl* なバッファー。sbcl側での評価結果が親側に割り込んだ形で表示されてる。しかし親側のメッセージは、その場でエコーされちゃって、子には届いていない。 哀れなものよのう。

いや、違うな、親側のメッセージはネットワーク経由で子側に届き、そこで機械的に評価されて、そのまま結果がネットワークを通じて返ってくるだけ。よもや親からのメッセージである事すら認識出来ない。

なんか、浪花節の世界だなあ。なんでパイプなんて施設してあるの? ネットワークが切れた場合の事を想定してるのかな?

TCPコネクションを強制切断してみる。

(base) sakae@debian:~$ sudo tcpkill -i lo port 34373
tcpkill: listening on lo [port 34373]
127.0.0.1:44164 > 127.0.0.1:34373: R 1278036081:1278036081(0) win 0
127.0.0.1:44164 > 127.0.0.1:34373: R 1278039492:1278039492(0) win 0
 :

待ちに入るので、何か通信(slime-replで何か評価)すると、tcpkillが発動して、絆を見るも無残に破壊してくれる。でも、pipeの非常通信は発動せず。

Not connected. Use ‘M-x slime’ to start a Lisp.

こんなメッセージがミニバッファに出て来たぞ。 仰せに従ってslimeを再起動したら復活した。子はそのまま生きていたぞ。そりゃそうだわな。子が生きていれば、新たな通信路を要求するだけですから。

とまあ、普段気にも留めない事を確かめてみました。tcpkillなんて言う危ない物も知ったし、これで佳しとするか。