LAMP ならぬ LIMP(3)

新築中のFreeBSD9の最後の仕上げは、python環境を整える事。unixでもWindowsでも(ほぼ)平等に 扱うには最適と思ったからだ。特に、去年やったPythonだけを使ってグラフを書くくだりは 何物にも変えられないぞ。

どんな物を入れたかと言うと

[sakae@secd /var/db/pkg]$ ls -d *py*
ipython-py27-0.11/              py27-pytz-2011n/
py27-cairo-1.8.10/              py27-pyzmq-2.1.10/
py27-dateutil-1.5/              py27-qt4-core-4.8.5,1/
py27-gobject-2.28.6/            py27-qt4-gui-4.8.5,1/
py27-gtk-2.24.0/                py27-qt4-svg-4.8.5,1/
py27-imaging-1.1.7_1/           py27-scientific-2.8/
py27-matplotlib-1.0.1_3/        py27-setuptools-0.6c11_1/
py27-nose-1.1.2/                py27-sip-4.12.4,1/
py27-numpy-1.6.1,1/             py27-sqlite3-2.7.2_1/
py27-pexpect-2.4_1/             py27-tkinter-2.7.2_3/
py27-pygments-1.4/              python27-2.7.2_3/
py27-pythontidy-1.21/          

ああ、上記リストを見てて、py-serialとかpy-usbが欠落してる事に気づいたよ。後で入れて おこうっと。

最近は、SICPをSchemeじゃなくてPythonでやる ようなご時勢だから、迷わずPyhonやっとけと、MITの人もUCBの人も言ってますな。

このご時勢と言うと、HAMの世界もハード一辺倒から、ソフトとハードが協調して動く FLEX-1500? QRP Software Defined Radio もににシフトしてる。こういうのが日本のメーカーから出てこないって、しゃちょさんが、球の 世界(か、よくても石)で育ったせいなのかな?

キーチェンジ

Limpのマニュアルを見てると

For sending code to a Lisp.

    * \ec: (Evaluate Current): Evaluate the current form
    * \et: (Evaluate current Top level form): Evaluate the currrent form to the top level
    * \ex: (Evaluate eXpression): Prompt for arbitrary expression to evaluate
    * \eb/ec/et: (Evaluate currently selected Block): Evaluate the currently visual block
    * \lf: (Load File): Load the current file in Lisp
    * \la: (Load Any file): Load any version (.lisp, .fasl, ...) of this file
    * \cf: (Compile File): Compile the current file
    * \cl: (Compile and Load): Compile current file and load
    * \ar: (Abort Reset): Send ABORT to Lisp
    * \ai :(Abort Interrupt): Send C-c to Lisp

こんな具合に、接頭語として、バックスラッシュを付ける事になっている。でも、バックスラッシュ なんてヤダって御人にはカスタマイズの道が紹介されたよ。

The rest of the bindings assumes <LocalLeader> == "\", which is the default. 
Some people prefer let maplocalleader = ",".

この変更を、keys.vimの頭にでも書いておけば、指使いがslimeっぽくなって、すこぶる便利だよ。 こういうちょい技の説明がvimの魔導書にあるかと思って探してみたら、ちゃんと有った。

					*<Leader>* *mapleader*
マップコマンドで特別な文字列 "<Leader>" を使用すると、その部分が変数
"mapleader" に設定された文字列で置き換わります。"mapleader" が空文字列のときや
設定されていない場合にはバックスラッシュが使用されます。例: >
	:map <Leader>A  oanother line<Esc>
これは次のものと同じ意味です: >
	:map \A  oanother line<Esc>
しかし次のように設定したあとでは: >
	:let mapleader = ","
次のものと同じ意味になります: >
	:map ,A  oanother line<Esc>

Note: 変数 "mapleader" はマップを定義するときに使用されます。"mapleader"を変更
しても、すでに定義されているマップには影響しません。

					*<LocalLeader>* *maplocalleader*
<LocalLeader> は <Leader> に似ていますが、"mapleader" ではなく
"maplocalleader" を使用します。<LocalLeader> はバッファローカルのマップに使用
するといいでしょう。例: >
      :map <LocalLeader>q  \DoItNow
<
グローバルプラグインでは <Leader> を使用し、ファイルタイププラグインでは
<LocalLeader> を使用するといいでしょう。"mapleader" と "maplocalleader" の設定
は同じでも構いませんが、別の値を設定すれば、グローバルプラグインとファイルタイ
ププラグインのマップが重なる可能性が低くなります。設定例としては、"mapleader"
をバックスラッシュのままにしておいて、"maplocalleader" をアンダースコア (_) に
するなど。

S式の長い旅

そんじゃ、keys.vimを基点に、S式を評価する流れを追ってみるかな。

" Eval Top:           send top-level s-exp to Lisp
" Eval Current:       send current s-exp to Lisp
" Eval Expression:    send arbitrary code to Lisp
nmap <buffer> <LocalLeader>et      <Plug>EvalTop
nmap <buffer> <LocalLeader>ec      <Plug>EvalCurrent
nmap <buffer> <LocalLeader>ex      <Plug>EvalExpression
:vimgrep EvalCurrent *.vim

すると、橋.vimに飛んで行き、一段nmapを経由して、

function! LimpBridge_eval_current_form()
  " save position
  let pos = LimpBridge_get_pos()

  " find & yank current s-exp
  normal! [(
  let sexp = LimpBridge_yank( "%" )
  call LimpBridge_send_to_lisp( sexp )
  call LimpBridge_goto_pos( pos )
endfunction

へ、行き着いた。S式を見つける時、ポンポンとカーソルが動き回るんで、今いるカーソルの 場所を保存してから、作業にかかるんだな。そして、S式を見つけて、それをLispに送り付ける。 終わったらカーソル位置を戻して終了とな。

それでは、LimpBridge_send_to_lisp の核心部分を見ておく。エラー対処の為、try catchで 囲まれた中。

  " goto LimpBridge_channel, delete it, put s-exp, write it to lisp
  try
      exe "hide bu" s:limp_bridge_channel
      exe "%d"
      normal! 1G

      " tried append() -- doesn't work the way I need it to
      let old_l = @l
      let @l = a:sexp
      normal! "lP
      let @l = old_l

      silent exe 'w!'
      call system('screen -x '.s:limp_bridge_screenid.' -p 0 -X eval "readbuf" "paste ."')

limp_bridge_channelのバッファーを隠した状態にして、全行削除、カーソルを 冒頭に移す。

続いて、レジスタLの値をセーブしてから、S式をレジスタに書き込み、そのレジスタ値をチャネル にペースト。レジスタ値を復帰。

チャネルファイルに書き込んでから、スクリーンコマンドを実行。

screenのマニュアルを見ると

       readbuf [-e encoding] [filename]

       Reads the contents of the specified file into the  paste  buffer.   You
       can tell screen the encoding of the file via the -e option.  If no file
       is specified, the screen-exchange filename is used.  See also  "buffer-
       file" command.

       paste [registers [dest_reg]]

       Write the (concatenated) contents of the  specified  registers  to  the
       stdin  queue  of the current window. The register '.' is treated as the
       paste buffer. If no parameter is given the user is prompted for a  sin-
       gle  register  to paste.  The paste buffer can be filled with the copy,
       history and readbuf commands.  Other registers can be filled  with  the
       register, readreg and paste commands. 

こんな事が書かれている。S式はvimのバッファーから拾い出されて、長い旅の末にsbclの replに(あたかもキーボードを叩いたがごとく)届くんだな。

上で出てきたチャネルファイル(= screen側のbufferfile)がどうなってるか調べてみたら、lisp.shで作られていた。 また、screenのステータスラインも、ここで作っているようだ。

    LIMP_BRIDGE_CHANNEL="$HOME/.limp_bridge_channel-$name.$id"
    touch $LIMP_BRIDGE_CHANNEL

    # magic goes here
    if [[ "$CORE_PATH" != "" ]]; then
        lisp="$CORE_PATH ($lisp)"
    else
        lisp="($lisp)"
    fi
    screen -x $STY -p 0 -X eval "hardstatus alwayslastline \"%{= bW}Limp: F12 to hide. %30= $lisp %= $name ($id)\""
    screen -x $STY -p 0 -X eval "bufferfile $LIMP_BRIDGE_CHANNEL"
    screen -x $STY -p 0 -X eval "register . $STY"
    screen -x $STY -p 0 -X eval "writebuf $LIMP_SCREEN_STY_FILE"

なかなか巧みな事をするわい。

vim内の事

これでS式がreplに届くしかけが分かったので、次は、上で飛ばしてきた(多分)vim内で行われて いる事に目を向けてみる。

最初は呪文なような、normal! [( というやつ。:h [( してみたら、移動コマンドの一種と いう事が判明した。

[(			[count] 前のマッチするものがない '(' に移動します。
			|exclusive|{Vi にはない機能です}
						*[{*
[{			[count] 前のマッチするものがない '{' に移動します。
			|exclusive|{Vi にはない機能です}

						*])*
])			[count] 次のマッチするものがない ')' に移動します。
			|exclusive|{Vi にはない機能です}

						*]}*
]}			[count] 次のマッチするものがない '}' に移動します。
			|exclusive|{Vi にはない機能です}

上の4つのコマンドは現在のコードブロックの最初か最後に移動するのに使うことがで
きます。'(', ')', '{', '}' 上で "%" コマンドを実行するのに似ていますが、この場
合コードブロック内のどこにいても実行することができます。C プログラムで非常に役
に立ちます。例: "case x:" にいる時に "[{" を実行することで switch 文の所に戻り
ます。

Oh! viには括弧目指して飛んでいく % しかなかったけど、vimでは拡張されてて より括弧族に優しくなってるのね。

次は本命のS式を取り出してくる所ね。予想では、ビジアルモードに入って 尻尾の括弧まで選択し、それをコピーすると思うんだ。さて、どうなっているやら?

function! LimpBridge_yank( motion )
  let value = ''

  let p = LimpBridge_get_pos()
  silent! exec 'normal!' a:motion
  let new_p = LimpBridge_get_pos()

  " did we move?
  if p != new_p
      " go back
      silent! exec 'normal!' a:motion

      let old_l = @l
      exec 'normal! "ly' . a:motion
      let value = @l
      let @l = old_l
  endif

  call LimpBridge_goto_pos( p )

  return( value )
endfunction

知らない単語が出てきたので、辞書を引いてみます。(受験シーズンですからね)

["x]y{motion}		{motion} のテキストを [レジスタ x に] コピーする。
			コピーされるテキストがないとき (例えば 1 桁目で "y0"
			を行う)、オプション 'cpoptions' がフラグ 'E' を含んで
			いるならエラーになる。

まるでアセンブラみたいに丁寧に作られてますなあ。(外側を壊さないように、 来た時のまんまの状態にしてるよ。ああ、川原でバーベキューをやって、ゴミ を放置する礼儀知らずに、爪のあかでも飲ませたいよ。と、年寄りの嘆き節)

ついでに、LimpBridge_get_pos()が何をやってるか眺めたら、どうも複雑な値を 返却してるみたいなので、vim語のreplで確かめてみる。

:echo LimpBridge_get_pos()
4|285,302,1

調べようとする関数名もTABで補完してくれるし、実行履歴もUp,downキーで選択 出来るし、なかなか便利だわい。