よろずや

Lisp 開発環境

ILISP - emacs + ilisp

SLIME - emacs + slime

xyzzy

ILISP - emacs + ilisp

編集とか

こんなコードがあったとする.

;; -*- mode: lisp -*-
(defun range (n &key (beg 1) (step 1))
    (let ((acc nil))
          (dotimes (i n (reverse acc))
                  (push (+ (* i step) beg) acc))))

S 式用の,Emacs の標準機能を以下に示す.

(*移動*)
ESC C-a : beginning-of-defun : defun の先頭へ移動
ESC C-e : end-of-defun : defun の末尾へ移動
ESC C-f : forward-sexp : カーソル位置から始まる S 式の末尾に移動
ESC C-b : backward-sexp : カーソル位置より一つ後の S 式の先頭に移動
ESC C-d : down-list : 1 レベル内側の S 式へ移動
ESC C-u : backward-up-list : 1 レベル外側の S 式へ移動
ESC C-n	: forward-list : 括弧のグループを超えて前方へ移動
ESC C-p	: backward-list : 括弧のグループを超えて後方移動
ESC C-k : kill-sexp : カーソル位置から始まる S 式を kill する
ESC C-backspace : backward-kill-sexp : カーソル位置より一つ後の S 式を kill する
(*インデント*)
割り当て無し : indent-sexp : S 式内を indent する
(*マーク*)
ESC C-SPC : mark-sexp : 引数の数だけ S 式をマークする
ESC C-h : mark-defun : defun 全体をマークする
(*編集*)
ESC C-t : transpose-sexps : ポイント位置前後の S 式を入れ換える
C-x n d : narrow-to-defun : defun の範囲に narrow する
C-x n w	: widen : narrow-to-defun を解除

のように,標準で S 式を編集する為のコマンド群が揃っている.

S 式単位の移動を行なっている画面写真を以下に示す.

image/edit000.png image/edit001.png image/edit002.png image/edit003.png

S 式単位の移動

ポイント位置から C-u ESC C-SPC で S 式 2 つ分マークしたのち,C-xC-x で移動

image/edit004.png image/edit005.png

S 式単位のマーク

このように,Emacs は標準で S 式単位の操作を行なえる.こういった機能を使いこなせば, 一見とっつきにくい S 式でも快適に作業がおこなえる.特に「S 式」という単位で移動できる というのは Lisp の括弧の特権であろう.括弧の対応によって簡単に要素を移動できる!! 中置記法とちがって優先度で頭を捻る必要もない. (とかいっときながら,Python も使ってるけど…あれは括弧が無いのが良い.)

ヘルプとかドキュメントとか

カーソルを合わせて…

hyperspec-lookup

ilisp から利用可能な 1 ドキュメントとしては,

が ilisp からヘルプとして利用可能です.HyperSpec のが新しい規格書です が,ドキュメントやサンプルが沢山あったほうが嬉しいので CLtL2 も入手しておき ましょう .

ilisp の設定を済ませると,コマンド一発で CLtL2 や HyperSpec が引けるように なります.HTML を表示時に外部ブラウザが起動してうっとおしい人は lynx や w3m を使 えば Emacs 内で全てが完結します.

また,通常の ilisp では関数名を入力した後のスペースで Window がポップアップ して引数リストを表示してくれますが,これが邪魔という人は

(setf lisp-no-popper t)

とすればポップアップしなくなります.さらに,引数をミニバッファに表示するのす ら邪魔だ,という人は

(setf ilisp-*arglist-message-lisp-space-p* nil)

で表示しないように設定できます.

ilisp 環境では

で,簡単にヘルプが引けます.

[1] ちなみに,デフォルトではネットワーク経由で使用するように設定されていますので,ネットワークに繋っている環境では何も考えずに利用できます.ネットワークに常時繋がっていない人はローカルにダウンロードして common-lisp-hyperspec-root を設定しましょう.

ilisp の機能とか

とりあえず,現状使ってる ilisp の機能.(broken?) は設定が悪いのかちゃんと動 いてないやつ.(だれか教えて下さい…)

PREFIX : C-c
C-c C-s         slow-lisp               安全第一に
C-c C-f         fast-lisp               速度重視で
C-c L           cltl2-lookup            CLtL2 を引く
C-c H           hyperspec-lookup        HyperSpec を引く
C-c k           compile-file-lisp       ファイルをコンパイル
C-c l           load-file-lisp          ファイルをロード
C-c S           select-ilisp            lisp を切り替える(複数の Lisp 処理系を使ってる時とか)
C-c s           status-lisp             ilisp の状態を表示
C-c g           abort-commands-lisp     lisp に送ったコマンドを中断
C-c z           reset-ilisp             lisp の状態をリセット
C-c y           call-defun-lisp         (broken?)
C-c b           switch-to-lisp          lisp なバッファに切り換え
C-c *           Prefix Command
C-c SPC         mark-change-lisp        変更マークを付ける
C-c !           default-directory-lisp  lisp のデフォルトディレクトリを表示
C-c t           trace-defun-lisp        関数のトレース ON/OFF
C-c C-w         compile-region-and-go-lisp      リージョンをコンパイル後,lisp バッファへ移動
C-c C-n         eval-next-sexp-and-go-lisp      S 式を評価後,lisp バッファへ移動
C-c C-e         eval-defun-and-go-lisp          関数定義を評価後,lisp バッファへ移動
C-c C-r         eval-region-and-go-lisp         リージョンを評価後,lisp バッファへ移動
C-c c           compile-defun-lisp              関数定義をコンパイル
C-c C-b         ilisp-compile-buffer            バッファにあるコードをコンパイル
C-c w           compile-region-lisp             リージョン内のコードをコンパイル
C-c n           eval-next-sexp-lisp             次の S 式を評価
C-c e           eval-defun-lisp                 関数定義を評価
C-c r           eval-region-lisp                リージョンを評価
C-c ^           edit-callers-lisp               (broken?)
C-c M           macroexpand-lisp                マクロ展開
C-c m           macroexpand-1-lisp              マクロ展開(macroexpand-1 ふつーこっち)
C-c d           documentation-lisp              ドキュメント表示
C-c a           arglist-lisp                    引数リスト表示
C-c I           inspect-lisp                    インスペクタ表示
C-c i           describe-lisp                   describe する
C-c )           find-unbalanced-lisp            括弧の不一致を見つける
C-c ;           comment-region-lisp             リージョンをコメントアウト
C-c <return>    complete                        補完
C-c TAB         complete-lisp                   lisp シンボルを補完
C-c C-c         compile-defun-and-go-lisp       関数定義をコンパイル後,lisp なバッファに移動
C-c C-z         run-lisp                        lisp を起動

ESC RET         complete                        補完
ESC TAB         complete-lisp                   lisp シンボルを補完
ESC `           next-caller-lisp                (broken?)
ESC "           replace-lisp                    置換
ESC ?           search-lisp                     (broken?)
ESC .           edit-definitions-lisp           (broken?)
ESC ,           next-definition-lisp            (broken?)
ESC C-e         end-of-defun-lisp               関数定義の末尾へ移動
ESC C-a         beginning-of-defun-lisp         関数定義の先頭へ移動
ESC C-q         indent-sexp-ilisp               S 式内をインデント
ESC C-r         reposition-window-lisp          位置を再設定
ESC q           reindent-lisp                   再インデント
ESC C-x         eval-defun-lisp                 関数定義を評価

C-c * 0         clear-changes-lisp              変更マークをクリア
C-c * c         compile-changes-lisp            変更マークしたところをコンパイル
C-c * e         eval-changes-lisp               変更マークしたところを評価
C-c * l         list-changes-lisp               変更マークのリスト表示

SLIME - emacs + slime

SLIME について

SLIME の画面

http://common-lisp.net/project/slime/ から入手可能.いろいろと面白い機能があ る.メインの環境が CMUCL なこともあって,最近は ILISP から SLIME に乗りかえ 気味.現在は CMUCL, SBCL, OpenMCL といった処理系に対応.まだ正式公開以前の版 であるため,CVS の先端を取ってくるとたまに壊れているときがある.

SLIME の特徴は,

SLIME のセットアップ

README によれば,ダウンロードした slime を展開したら,

(add-to-list 'load-path "/the/path/to/this/directory")
(require 'slime)
(add-hook 'lisp-mode-hook (lambda ()
                            (slime-mode t)
                            (show-paren-mode)))

とするだけ.Common Lisp らしいインデントにするには↓をどーぞ.(私の趣味ですが)

(add-hook 'slime-mode-hook
	  (lambda ()
	    (setq lisp-indent-function 'common-lisp-indent-function)))

;; Additional definitions by Pierpaolo Bernardi.
(defun cl-indent (sym indent)
  (put sym 'common-lisp-indent-function
       (if (symbolp indent)
	   (get indent 'common-lisp-indent-function)
	 indent)))

(cl-indent 'if '1)
(cl-indent 'generic-flet 'flet)
(cl-indent 'generic-labels 'labels)
(cl-indent 'with-accessors 'multiple-value-bind)
(cl-indent 'with-added-methods '((1 4 ((&whole 1))) (2 &body)))
(cl-indent 'with-condition-restarts '((1 4 ((&whole 1))) (2 &body)))
(cl-indent 'with-simple-restart '((1 4 ((&whole 1))) (2 &body)))

SLIME のキーバインド

slime.el より.

  '((" "        . slime-space)         ;; 関数名の後等でミニバッファに情報を表示するためのもの.
    ("\M-p"     . slime-previous-note) ;; 注釈(コンパイル時等のエラーや警告)にジャンプできる.
    ("\M-n"     . slime-next-note)     ;; 次の注釈にジャンプできる.
    ("\C-c\M-c" . slime-remove-notes)  ;; 注釈を消す
    ("\C-c\C-k" . slime-compile-and-load-file) ;; カレントバッファと対応するファイルをコンパイルした後ロードする
    ("\C-c\M-k" . slime-compile-file)          ;; ファイルをコンパイル
    ("\C-c\C-c" . slime-compile-defun)         ;; 関数定義をコンパイル
    ("\C-c\C-l" . slime-load-file)             ;; ファイルをロード
    ;; Multiple bindings for completion, since M-TAB is often taken by
    ;; the window manager.
    ("\M-\C-i"  . slime-complete-symbol)  ;; シンボル補完
    ("\C-c\C-i" . slime-complete-symbol)  ;; シンボル補完(M-TAB が Window Manger に食われちゃってる場合)
    ("\M-."     . slime-edit-fdefinition) ;; 関数定義へジャンプ
    ("\M-,"     . slime-pop-find-definition-stack) ;; M-. の移動を逆にたどる
    ("\C-x\C-e" . slime-eval-last-expression)      ;; 式を評価する
    ("\C-c\C-p" . slime-pprint-eval-last-expression) ;; 式を評価して結果を表示
    ("\M-\C-x"  . slime-eval-defun)                ;; 関数定義を評価する
    ("\C-c:"    . slime-interactive-eval)          ;; ミニバッファにプロンプトを出し,入力された式を評価
    ("\C-c\C-z" . slime-switch-to-output-buffer)   ;; slime の出力バッファに移動
    ("\C-c\C-d" . slime-describe-symbol)           ;; シンボルを describe した結果を表示
    ("\C-c\M-d" . slime-disassemble-symbol)        ;; シンボルを disassemble した結果を表示
    ("\C-c\C-t" . slime-toggle-trace-fdefinition)  ;; 関数の TRACE を ON/OFF する
    ("\C-c\C-a" . slime-apropos)                   ;; apropos
    ("\C-c\M-a" . slime-apropos-all)               ;; ↑との違いが??
    ([(control c) (control m)] . slime-macroexpand-1)    ;; マクロを展開 (macroexpand-1)
    ([(control c) (meta m)]    . slime-macroexpand-all)  ;; マクロを展開 (macroexpand)
    ("\C-c\C-g" . slime-interrupt)  ;; SLIME に割り込む
    ("\C-c\M-g" . slime-quit)       ;; SLIME を終了
    ("\C-c\M-0" . slime-restore-window-configuration) ;; ?
    ("\C-c\C-h" . hyperspec-lookup)                   ;; HyperSpec を引く
    ("\C-c\C-wc" . slime-who-calls)                   ;; ?
    ("\C-c\C-wr" . slime-who-references)              ;; ?
    ("\C-c\C-wb" . slime-who-binds)                   ;; ?
    ("\C-c\C-ws" . slime-who-sets)                    ;; ?
    ("\C-c\C-wm" . slime-who-macroexpands)            ;; ?
    ;; Not sure which binding is best yet, so both for now.
    ([(control meta ?\.)] . slime-next-location)      ;; ?
    ("\C-c\C- "  . slime-next-location)               ;; ?
    ("\C-c~"     . slime-sync-package-and-default-directory) ;; パッケージとディレクトリをファイルに合わせる

xyzzy - Common Lisp なエディタ

xyzzy の画面

Windows で Lisp と言えばこれ.xyzzy が素晴しいです. なんと Common Lisp (準拠度 6 割程度とのこと)を採用した高性能エディタです.

設定

xyzzy は標準で lisp モードを持っていますが,何故かキーワードハイライトが OFF になってます. キーワードファイル自体は etc/lisp に存在しているので,lisp-mode のフックで有効にしてやれば OK です.

(add-hook '*lisp-mode-hook*
  #'(lambda ()
       (make-local-variable 'keyword-hash-table)
       (setf keyword-hash-table (load-keyword-file "lisp"))
       (make-local-variable 'regexp-keyword-list)
       (setf regexp-keyword-list
          (compile-regexp-keyword-list
            '(("(" t (:color 14))
              (")" t (:color 14)))))))

また,Common Lisp で開発する時に必須の HyperSpec を引けるように hyperspec.el を Emacs から移植しました.