[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17. Lispプログラムのデバッグ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Debugging"
"elisp/Lispプログラムのデバッグ"へのコメント(無し)

Emacs Lispプログラムの問題点を調べるには、 問題が発生したときにどのようにプログラムを使っているかに依存して、 3つの方法があります。

他の有用なデバッグツールは、ドリブルファイルです。 ドリブルファイルをオープンしてあると、 Emacsはすべてのキーボード入力をこのファイルにコピーします。 あとでこのファイルを調べれば、どんな入力があったかわかります。 See 節 37.8 端末入力

端末設定に関した問題を解決するには、 関数open-termscriptが有用です。 See 節 37.9 端末出力



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1 Lispデバッガ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Debugger"
"elisp/Lispデバッガ"へのコメント(無し)

通常のLispデバッガは、フォームの評価を一時停止する機能を提供します。 評価を一時停止しているあいだ(ブレーク(break)と呼ばれる状態)は、 実行時スタックを調べたり、ローカルやグローバル変数の値を調べたり、 それらの値を変更できます。 ブレークは再帰編集なので、Emacsの通常の編集機能すべてを使えます。 デバッガを再帰的に起動するようなプログラムを実行することさえできます。 See 節 20.11 再帰編集

17.1.1 エラーによるデバッガの起動    Entering the debugger when an error happens.
17.1.2 無限ループのデバッグ    Stopping and debugging a program that doesn't exit.
17.1.3 関数呼び出し時のデバッガの起動    Entering it when a certain function is called.
17.1.4 デバッガの明示的な起動    Entering it at a certain point in the program.
17.1.5 デバッガの使い方    What the debugger does; what you see while in it.
17.1.6 デバッガコマンド    Commands used while in the debugger.
17.1.7 デバッガの起動    How to call the function debug.
17.1.8 デバッガの内部    Subroutines of the debugger, and global variables.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.1 エラーによるデバッガの起動

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Error%20Debugging"
"elisp/エラーによるデバッガの起動"へのコメント(無し)

デバッガへ入るもっとも重要な時期は、Lispエラーが発生したときです。 これにより、エラーの直接原因を調べることができます。

しかし、デバッガに入るのは、エラーの通常の帰結ではありません。 多くのコマンドは (バッファの末尾でC-fを使うなどの)不適切に起動されると しばしばLispエラーを生じますが、 通常の編集ではそのたびにデバッガに入ったのではとても不便です。 そのため、エラーによってデバッガに入りたい場合には、 変数debug-on-errornil以外を設定します。 (コマンドtoggle-debug-on-errorはこれを簡単に行う。)

User Option: debug-on-error
この変数は、エラーが通知され処理されないときに デバッガを呼び出すかどうか決定する。 debug-on-errortであると、 すべての種類のエラー(debug-ignored-errorsに指定したものを除く) はデバッガを呼び出す。 nilであるとデバッガを呼び出さない。

その値はデバッガを呼び出すエラー条件のリストでもよい。 たとえば、リスト(void-variable)にすると、 「値を持たない変数に関したエラー」のみがデバッガを起動する。

この変数がnil以外であると、 Emacsはプロセスのフィルタ関数や番兵に対してエラーハンドラを作成しない。 したがって、これらの関数でのエラーもデバッガを起動する。 see 節 36. プロセス。

User Option: debug-ignored-errors
この変数は、デバッガに入らないエラーの種類を指定する。 その値はエラー条件シンボルや正規表現のリストである。 エラーにこれらの条件シンボルが含まれるか エラーメッセージが正規表現の1つに一致する場合には、 debug-on-errorの値に関わらず 当該エラーではデバッガに入らない。

この変数の通常の値は、 編集ではしばしば発生するが、Lispプログラムのバグではほとんど発生しないような エラー群のリストである。 しかし、『ほとんど』は『けっして』ではない。 このリストに一致するようなエラーで読者のプログラムが失敗する場合、 エラーをデバッグするにはこのリストを変更する必要がある。 もっとも簡単な方法は、debug-ignored-errorsnilを 設定することである。

User Option: debug-on-signal
通常、condition-caseで捕捉したエラーは、 たとえdebug-on-errornil以外であっても、 けっしてデバッガを起動しない。 いいかえれば、デバッガを起動するまえに、 condition-caseはエラー処理の機会を得るのである。

debug-on-signalnil以外の値を設定すると、 各エラーごとにデバッガがまず機会を得る。 debug-on-errordebug-ignored-errorsの値で指定される 条件に一致すれば、condition-caseに関わらず エラーはデバッガを起動する。

警告: この変数は強力な処方である! Emacsのさまざまな部分では通常の動作としてエラーを処理し、 読者にはエラーが発生したことさえわからない。 debug-on-signalnil以外の値を設定すると それらのエラーでデバッガに入る。

警告: debug-on-errornilであると、 debug-on-signalは意味を持たない。

ファイル`.emacs'をロード中に発生するエラーをデバッグするには、 オプション`--debug-init'を使います。 これにより、`.emacs'のロード中はdebug-on-errortに束縛し、 初期化ファイルでのエラーを捕捉するcondition-caseを迂回します。

読者のファイル`.emacs'でdebug-on-errorを設定しても、 その効果は`.emacs'のロードを終ると持続しません。 (これはコマンド行オプション`--debug-init'の実装における 好ましくない特性である。) `.emacs'でdebug-on-errorを恒久的に設定する最良の方法は、 つぎのように、after-init-hookを用いることです。

 
(add-hook 'after-init-hook
          '(lambda () (setq debug-on-error t)))



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.2 無限ループのデバッグ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Infinite%20Loops"
"elisp/無限ループのデバッグ"へのコメント(無し)

プログラムが無限にループし戻ってこないときには、 まず、ループを停止する必要があります。 ほとんどのオペレーティングシステムでは、 中断を意味するC-gを使います。

普通に中断したのでは、 プログラムが無限ループした理由に関する情報は得られません。 より詳しい情報を得るには、 変数debug-on-quitnil以外を設定します。 C-gによる中断はエラーとは扱わないため、 C-gの処理に関してdebug-on-errorはなんの効果もありません。 同様に、debug-on-quitはエラーに関してなんの効果もありません。

無限ループの途中でデバッガを起動できれば、 デバッガでステップ実行コマンドを使って先へ進めます。 ループひとまわりをステップ実行すれば、 問題を解決するに十分な情報を得られるはずです。

User Option: debug-on-quit
この変数は、quitが通知され処理されなかった場合に、 デバッガを呼び出すかどうかを決定する。 debug-on-quitnil以外である場合、 (C-gを打って)中断するとデバッガを呼び出す。 debug-on-quitnilであると、 中断してもデバッガを呼び出さない。 see 節 20.9 中断



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.3 関数呼び出し時のデバッガの起動

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Function%20Debugging"
"elisp/関数呼び出し時のデバッガの起動"へのコメント(無し)

プログラムの途中で発生する問題点を調べるための1つの有用な技法は、 ある関数を呼び出すたびにデバッガに入ることです。 問題を生じる関数に対してこのようにしておき、 当該関数をステップ実行するか、あるいは、 問題が発生する直前に呼ばれる関数に対してこのようにしておき、 その関数の呼び出しを終えてから、呼び出し側をステップ実行します。

コマンド: debug-on-entry function-name
この関数は、function-nameが呼び出されるたびに デバッガを起動するようにする。 当該関数の定義の最初のフォームとして フォーム(debug 'debug)を挿入することでこれを行う。

Lispコードで定義した任意の関数は、 解釈実行コードであろうとコンパイル済みのコードであろうと、 関数に入るときにブレークするようにできる。 関数がコマンドであると、Lispから呼ばれたときや 対話的に呼ばれたときに(引数を読み取ってから)デバッガに入る。 (Cで書いた)基本関数は、この方法ではデバッグできない。

debug-on-entryを対話的に呼び出すと、 ミニバッファでfunction-nameを問い合わせる。 その関数がすでに呼び出し時にデバッガを起動するようになっていると、 debug-on-entryはなにもしない。 debug-on-entryはつねにfunction-nameを返す。

注意: debug-on-entryを使ったあとに当該関数を再定義すると、 デバッガに入るためのコードがなくなる。 実質的には、関数を再定義すると呼び出し時にブレークする機能を 取り消すことになる。

 
(defun fact (n)
  (if (zerop n) 1
      (* n (fact (1- n)))))
     => fact
(debug-on-entry 'fact)
     => fact
(fact 3)

------ Buffer: *Backtrace* ------
Entering:
* fact(3)
  eval-region(4870 4878 t)
  byte-code("...")
  eval-last-sexp(nil)
  (let ...)
  eval-insert-last-sexp(nil)
* call-interactively(eval-insert-last-sexp)
------ Buffer: *Backtrace* ------

(symbol-function 'fact)
     => (lambda (n)
          (debug (quote debug))
          (if (zerop n) 1 (* n (fact (1- n)))))

コマンド: cancel-debug-on-entry function-name
この関数は、function-nameに対するdebug-on-entryの効果 (呼び出し時にブレークする)を取り消す。 対話的に呼び出すと、 ミニバッファでfunction-nameを問い合わせる。 function-namenilであったり空文字列であると、 すべての関数について、呼び出し時にブレークすることを取り消す。

呼び出し時にブレークする設定をしていない関数に対して cancel-debug-on-entryを呼び出してもなにもしない。 つねにfunction-nameを返す。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.4 デバッガの明示的な起動

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Explicit%20Debug"
"elisp/デバッガの明示的な起動"へのコメント(無し)

読者のプログラムに式(debug)を書くと、 その箇所でデバッガを呼び出すことができます。 つまり、ソースファイルを訪問して適当な箇所にテキスト`(debug)'を挿入し、 C-M-xと打ちます。 警告: 一時的なデバッグ目的でこれを行う場合には、 ファイルを保存するまえにこの挿入箇所をもとに戻すこと!

`(debug)'を挿入する箇所は、 余分なフォームを評価してもその値を無視できる場所でなければなりません。 ((debug)の値が無視されないと、 プログラムの実行を変えてしまう!) もっとも適した一般的な場所はprognや暗黙のprognの内側です (see 節 9.1 逐次実行)。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.5 デバッガの使い方

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Using%20Debugger"
"elisp/デバッガの使い方"へのコメント(無し)

デバッガに入ると、それまで選択していたバッファをあるウィンドウに、 `*Backtrace*'という名前のバッファを別のウィンドウに表示します。 バックトレースバッファでは、各行は現在実行中のLisp関数の各レベルです。 このバッファの先頭には、デバッガを起動するに至った理由 (エラーで起動されたときにはエラーメッセージと関連データ) を表すメッセージがあります。

バックトレースバッファは読み出し専用であり、 各文字をデバッガコマンドであると定義した 特別なメジャーモード、debuggerモードを使います。 Emacsの通常の編集コマンドも使えます。 したがって、エラー発生時に編集していたバッファを調べるためにウィンドウを 切り替えたり、バッファを切り替えたり、ファイルを訪れたり、 その他のどんな編集でもできます。 しかし、デバッガは再帰編集レベル(see 節 20.11 再帰編集)であるので、 デバッグを終えるときには、バックトレースバッファに戻ってから デバッガを(コマンドqで)終了するのが賢い方法です。 デバッガを終了すると、再帰編集から抜けバックトレースバッファを削除します。

バックトレースバッファでは、実行中の関数とその引数の値を表示します。 また、スタックフレームを記述する行へポイントを移動することで スタックフレームを指定できます。 (スタックフレームとは、Lispインタープリタが関数の起動に関する情報を 記録しておく場所である。) ポイントがある行に対応するフレームをカレントフレーム(current frame)と 呼びます。 デバッガのある種のコマンドはカレントフレームに作用します。

デバッガ自身はバイトコンパイルしたものを実行する必要があります。 というのは、デバッガ自身が使用するスタックフレームのサイズを 仮定しているからです。 解釈実行だとこの仮定が成り立ちません。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.6 デバッガコマンド

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Debugger%20Commands"
"elisp/デバッガコマンド"へのコメント(無し)

デバッガ内(debuggerモード)では、 通常のカーソル移動コマンドに加えて以下の特別なコマンドを使えます。 (ウィンドウやバッファの切り替えなどのEmacsの通常の機能も使えることに留意。)

デバッガコマンドのもっとも重要な使い方はステップ実行であり、 これにより制御の流れを調べることです。 デバッガは、解釈実行版の関数の制御構造をステップ実行できますが、 バイトコンパイルした関数ではできません。 バイトコンパイルした関数をステップ実行したい場合には、 同じ関数を解釈実行版の定義に置き換える必要があります。 (これには、関数のソースを訪れて、その定義内でC-M-xと打つ。)

debuggerモードのコマンド一覧を以下に示します。

c
デバッガを終了し実行を継続する。 継続可能であれば、 (デバッガ内で行った変数値やデータ構造に対する変更などの副作用を除いて) デバッガを起動しなかったかのようにプログラムの実行を再開する。

継続が可能なのは、 関数呼び出し時や終了時、明示的な起動、中断によりデバッガに入った場合である。 エラーが原因でデバッガが起動されたときには継続できない。

d
実行を継続するが、任意のLisp関数を呼び出すとデバッガに入る。 これにより、式の部分式をステップ実行して 部分式が計算する値やその動作を調べることができる。

このようにしてデバッガを起動した関数呼び出しのスタックフレームには 自動的に印が付き、そのスタックから抜けるとデバッガがふたたび呼び出される。 この印を消すにはコマンドuを使う。

b
フレームから抜けるとデバッガに入るようにカレントフレームに印を付ける。 このように印を付けたフレームには、バックトレースバッファでは星印が付く。

u
カレントフレームから抜けるときにデバッガに入らない。 これは、当該フレームに対するコマンドbを取り消す。 視覚的にはバックトレースバッファの当該行から星印が取られる。

e
ミニバッファでLisp式を読み取り、それを評価し、その値をエコー領域に表示する。 この操作の一環として、デバッガは重要なある種の変数や カレントバッファを変更する。 eはそれらの値をデバッガの外側の値に一時的に復元するので、 それらを調べたり変更したりできる。 これによりデバッガは透過的になる。 対照的に、M-:はデバッガ内で特別なことは行わない。 デバッガ内での変数値を表示する。

R
eと同様であるが、 バッファ`*Debugger-record*'での評価結果も保存する。

q
デバッグ中のプログラムを終了する。 Emacsのトップレベルのコマンド実行へ戻る。

C-gでデバッガへ入ったが、 実際には中断したいのであってデバッグはしたくない場合には コマンドqを使う。

r
デバッガから値を指定して戻る。 その値は、ミニバッファで式を読み取り、それを評価して得る。

bで指定したりdでフレームに入ることで) Lispの呼び出しフレームから抜けでたためにデバッガが起動された場合に、 コマンドrは有用である。 コマンドrで指定した値は、当該フレームの値として使われる。 このコマンドは、debugを呼び出してその戻り値を使う場合にも有用である。 さもなければ、rcと同じ効果であり、指定した戻り値は関係ない。

エラーでデバッガに入った場合にはrは使えない。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.7 デバッガの起動

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Invoking%20the%20Debugger"
"elisp/デバッガの起動"へのコメント(無し)

ここでは、デバッガを起動するために使われる関数debugの詳細を述べます。

Function: debug &rest debugger-args
この関数はデバッガに入る。 `*Backtrace*'(あるいはデバッガの第2レベルに再帰的に入ると `*Backtrace*<2>'など)という名前のバッファに切り替え、 Lisp関数の呼び出しスタックに関する情報でこのバッファを満たす。 そして再帰編集に入りdebuggerモードのバックトレースバッファを表示する。

debuggerモードのコマンドcrで再帰編集から抜けだし、 debugはそれ以前のバッファに切り替え debugを呼び出したところへ戻る。 これは、関数debugが呼び出し側へ戻る唯一の手段である。

debugger-argsの使い途は、 debugが引数の残りをバッファ`*Backtrace*'の先頭に表示し、 ユーザーが読めるようにすることである。 以下に述べる場合を除いて、これがこれらの引数の唯一の用途である。

debugの第1引数が特定の値を持つ場合、特別な意味がある。 (通常、これらの値はEmacs内部で用いるだけであり、 プログラマがdebugを呼ぶときには使わない。) 以下にこれらの特別な値を示す。

lambda
第1引数がlambdaであると、 debug-on-next-callnil以外であるために 関数に入るときにdebugを呼び出したことを意味する。 デバッガはバッファの先頭にテキスト行`Entering:'を表示する。

debug
第1引数がdebugであると、 関数に入るときにデバッガを起動するようになっていたために debugを呼び出したことを示す。 デバッガは、lambdaの場合と同様に、`Entering:'を表示する。 さらに、当該関数のスタックフレームに関数から 戻るときにデバッガを起動するように印を付ける。

t
第1引数がtであると、 debug-on-next-callnil以外であるときに フォームの並びを評価したために debugを呼び出したことを示す。 デバッガはバッファの先頭行につぎの行を表示する。

 
Beginning evaluation of function call form:

exit
第1引数がexitであると、 スタックフレームから抜けるときにデバッガを呼び出すように印を 付けたスタックフレームから抜けたことを示す。 この場合、debugの第2引数はフレームからの戻り値である。 デバッガはバッファの先頭行に`Return value:'に続けて戻り値を表示する。

error
第1引数がerrorであると、 エラーやquitが通知されたが処理されないためにデバッガに入ったことを示し、 `Signaling:'に続けて通知されたエラーとsignalの引数を表示する。 たとえばつぎのとおり。

 
(let ((debug-on-error t))
  (/ 1 0))

------ Buffer: *Backtrace* ------
Signaling: (arith-error)
  /(1 0)
...
------ Buffer: *Backtrace* ------

エラーが通知されたときには、 変数debug-on-errornil以外であるはずである。 quitが通知されたときには、 変数debug-on-quitnil以外であるはずである。

nil
明示的にデバッガに入るときには、 debugger-argsの先頭としてnilを使う。 debugger-argsの残りはバッファの先頭行に表示される。 この機能を用いてメッセージを表示でき、 たとえば、debugを呼び出した条件の覚え書きにする。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.1.8 デバッガの内部

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Internals%20of%20Debugger"
"elisp/デバッガの内部"へのコメント(無し)

本節では、デバッガが内部的に使用する関数や変数について述べます。

Variable: debugger
この変数の値は、デバッガを起動するために呼び出す関数である。 その値は、可変個数の引数を取る関数(あるいは典型的には関数名)であること。 その関数でなんらかのデバッガに入ると仮定する。 この変数のデフォルト値はdebug

Lispが関数に渡す最初の引数で、呼び出した理由を表す。 引数の規約はdebugに記述してある。

コマンド: backtrace
この関数は、現在活性なLisp関数呼び出しのトレースを表示する。 これは、debugがバッファ`*Backtrace*'を 満たすために用いる関数である。 どの関数呼び出しが活性であるかを 判断するためにスタックを参照する必要があるためCで書いてある。 戻り値はつねにnil

以下の例では、Lisp式で明示的にbacktraceを呼び出す。 これにより、バックトレースをストリームstandard-outputに出力する。 ここではバッファ`backtrace-output'に出力する。 バックトレースの各行は、1つの関数呼び出しを表す。 関数の引数値すべてが判ればそれらを行に表示する。 それらが計算途中であれば、その旨を行に表示する。 スペシャルフォームの引数は省略する。

 
(with-output-to-temp-buffer "backtrace-output"
  (let ((var 1))
    (save-excursion
      (setq var (eval '(progn
                         (1+ var)
                         (list 'testing (backtrace))))))))

     => nil

----------- Buffer: backtrace-output ------------
  backtrace()
  (list ...computing arguments...)
  (progn ...)
  eval((progn (1+ var) (list (quote testing) (backtrace))))
  (setq ...)
  (save-excursion ...)
  (let ...)
  (with-output-to-temp-buffer ...)
  eval-region(1973 2142 #)
  byte-code("...  for eval-print-last-sexp ...")
  eval-print-last-sexp(nil)
* call-interactively(eval-print-last-sexp)
----------- Buffer: backtrace-output ------------

文字`*'は、 抜け出るときにデバッガを起動する印が付いているフレームを表す。

Variable: debug-on-next-call
この変数がnil以外であると、 つぎにevalapplyfuncallを呼び出すまえに デバッガを呼び出すことを指定する。 デバッガに入るとdebug-on-next-callnilに設定する。

デバッガのコマンドdは、この変数を設定することで動作する。

Function: backtrace-debug level flag
この関数は、levelの深さのスタックフレームに 値flagに応じてフレームから抜け出るときのデバッガ呼び出しの印を付ける。 flagnil以外であると、 のちに当該フレームから抜けるとデバッガに入る。 非ローカルな脱出で当該フレームから抜けるときにもデバッガに入る。

この関数はデバッガのみが使用する。

Variable: command-debug-status
この変数は、現在の対話的コマンドのデバッグ状況を記録する。 コマンドが対話的に呼び出されるたびに、 この変数はnilに束縛される。 デバッガはこの変数に設定することで、 同じコマンドの起動中にデバッガが将来起動された場合に備えて 情報を残すことができる。

デバッガにとっては、通常のグローバル変数ではなくこの変数を使う利点は、 以降のコマンド起動にデータが繰り越さないことである。

Function: backtrace-frame frame-number
関数backtrace-frameは、Lispデバッガで使うことを意図している。 深さframe-numberのスタックフレームで進行中の計算に関する情報を返す。

当該フレームで引数の評価を完了していなければ(あるいはスペシャルフォーム)、 値は(nil function arg-forms...)

当該フレームで引数の評価を完了し関数を呼び出していれば、 値は(t function arg-values...)

戻り値において、functionは評価したリストのCARであるか、 マクロ呼び出しではlambda式である。 関数に引数&restがあれば、リストarg-valuesの残りで表現される。

frame-numberが範囲外であると、backtrace-framenilを返す。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2 edebug

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug"
"elisp/edebug"へのコメント(無し)

edebugはEmacs Lispプログラムのソースレベルデバッガであり、 つぎのことを行えます。

以下の最初の3つの節では、edebugを使うのに十分な情報を与えます。

17.2.1 edebugの使い方    Introduction to use of Edebug.
17.2.2 edebug向けの処置    You must instrument your code in order to debug it with Edebug.
17.2.3 edebugの実行モード    Execution modes, stopping more or less often.
17.2.4 ジャンプ    Commands to jump to a specified place.
17.2.5 edebugのその他のコマンド    Miscellaneous commands.
17.2.6 ブレークポイント    Setting breakpoints to make the program stop.
17.2.7 エラーの捕捉    trapping errors with Edebug.
17.2.8 edebugのビュー    Views inside and outside of Edebug.
17.2.9 評価    Evaluating expressions within Edebug.
17.2.10 評価リストバッファ    Expressions whose values are displayed each time you enter Edebug.
17.2.11 edebugでの出力    Customization of printing.
17.2.12 トレースバッファ    How to produce trace output in a buffer.
17.2.13 カバレッジテスト    How to test evaluation coverage.
17.2.14 外側の文脈    Data that Edebug saves and restores.
17.2.15 マクロ呼び出しの処置    Specifying how to handle macro calls.
17.2.16 edebugのオプション    Option variables for customizing Edebug.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.1 edebugの使い方

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Using%20Edebug"
"elisp/edebugの使い方"へのコメント(無し)

edebugでLispプログラムをデバッグするには、 デバッグしたいLispコードをまず処置(instrument)しておく必要があります。 これを行う簡単な方法は、関数やマクロの定義にポイントを移動してから、 C-u C-M-x(前置引数を指定したeval-defun)を実行します。 コードを処置する別の方法については、See 節 17.2.2 edebug向けの処置

関数をいったん処置しておけば、当該関数を呼び出すとedebugを活性にします。 edebugが活性になると実行を停止し、 読者が選択したedebugの実行モードに応じて、 関数をステップ実行したりデバッグコマンドを検査しながら 表示を更新しつつ実行を継続します。 デフォルトの実行モードはステップ実行であり、 いったん実行を停止します。 See 節 17.2.3 edebugの実行モード

edebugでは、デバッグ中のLispコードのソースを 表示したEmacsバッファを読者は見ます。 このバッファをソースコードバッファと呼びます。 このバッファは一時的に読み出し専用です。

左端の矢印は、関数の実行中の行を表します。 ポイントの初期位置は関数の実行中の行にありますが、 読者自身がポイントを移動すると変わります。

(以下の)facの定義を処置してから(fac 3)を実行したとすると、 つぎのようになります。 ポイントはifのまえの開き括弧にあります。

 
(defun fac (n)
=>-!-(if (< 0 n)
      (* n (fac (1- n)))
    1))

edebugが関数内で実行を停止できる箇所を停止位置(stop point)と呼びます。 これらは、リストである各部分式の前後と各変数参照のうしろにあります。 関数facの中にある停止位置をピリオドで示します。

 
(defun fac (n)
  .(if .(< 0 n.).
      .(* n. .(fac (1- n.).).).
    1).)

ソースコードバッファでは、Emacsのlispモードのコマンドに加えて edebugの特別なコマンドを使えます。 たとえば、つぎの停止位置まで実行するには edebugコマンドSPCを打ちます。 facに入ったあとでSPCを1回打つと、 つぎのような表示になります。

 
(defun fac (n)
=>(if -!-(< 0 n)
      (* n (fac (1- n)))
    1))

式のうしろでedebugが実行を停止すると、 式の値をエコー領域に値を表示します。

多用される他のコマンドには、 停止位置にブレークポイントを設定するb、 ブレークポイントに達するまで実行するg、 edebugを終了してトップレベルのコマンドループへ戻るqがあります。 edebugのコマンド一覧を表示するには?を打ちます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.2 edebug向けの処置

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Instrumenting"
"elisp/edebug向けの処置"へのコメント(無し)

Lispコードのデバッグにedebugを使うためには、 コードをまず処置する必要があります。 コードを処置すると、適当な箇所でedebugを起動する追加のコードを挿入します。

いったんedebugをロードすると、 コマンドC-M-xeval-defun)は再定義されます。 定義内で前置引数を指定して起動すると 定義を評価するまえに処置するようになります。 (ソースコード自体は変更しない。) 変数edebug-all-defsnil以外であると、 前置引数の意味を逆にします。 つまり、前置引数を指定しない限りC-M-xは関数定義を処置します。 変数edebug-all-defsのデフォルト値はnilです。 コマンドM-x edebug-all-defsは変数edebug-all-defsの値を トグルします。

edebug-all-defsnil以外であると、 コマンドeval-regioneval-current-buffereval-bufferも それらが評価する定義を処置します。 同様に、edebug-all-formsは、 定義以外のフォームであってもeval-region任意のフォームを処置するかどうか制御します。 これは、ミニバッファでのロードや評価には適用されません。 コマンドM-x edebug-all-formsはこのオプションをトグルします。

別のコマンドM-x edebug-eval-top-level-formは、 edebug-all-defsedebug-all-formsの値に関わらず 任意のトップレベルのフォームを処置するために使えます。

edebugが動作中は、 コマンドIedebug-instrument-callee)で、 ポイントのうしろのフォームから呼ばれる関数やマクロの定義を 処置済みでなければ処置できます。 これは、edebugが当該関数のソースを探せる場合にのみ可能です。 edebugをロード後には、 eval-regionは、処置していないものも含めて、 評価した各定義の位置を記録しています。 関数を処置後に呼び出してステップ実行する コマンドi(see 節 17.2.4 ジャンプ)も参照してください。

edebugは、標準のスペシャルフォームすべて、 式を引数とするinteractiveフォーム、 無名ラムダ式、他の定義フォームをどのように処置するかわかっています。 edebugは、マクロ呼び出しを引数に持つユーザー定義マクロをどのように 処置すべきかわかりませんから、読者がそれを指示する必要があります。 詳しくは、See 節 17.2.15 マクロ呼び出しの処置

edebugは、あるセッションで初めてコードを処置する場合、 フックedebug-setup-hookを実行してから それにnilを設定します。 これを利用すると、読者が使用するパッケージに対応した edebug用仕様(see 節 17.2.15 マクロ呼び出しの処置)を edebugを使用する場合にのみロードするようにできます。

定義から処置を取り除くには、 処置しないような方法でその定義を単に再評価するだけです。 けっして処置せずにフォームを評価する方法は2つあります。 ファイルをloadするか、 ミニバッファでeval-expressionM-:)を使います。

edebugが処置中に構文エラーを検出すると、 コードのエラー箇所にポイントを置いて、 エラーinvalid-read-syntaxを通知します。

edebugの内側で使える他の評価関数についてはSee 節 17.2.9 評価



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.3 edebugの実行モード

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Execution%20Modes"
"elisp/edebugの実行モード"へのコメント(無し)

edebugには読者がデバッグしているプログラムを実行するための 実行モードが複数あります。 これらをedebugの実行モードと呼びます。 これらをメジャーモードやマイナモードと混同しないでください。 edebugの実行モードは、停止するまでにどの程度edebugが実行を継続するか、 たとえば、各停止位置で停止するのかつぎのブレークポイントまで継続するのか、 また、停止するまでに評価の進行状況をどの程度edebugが表示するのかを 決定します。

通常、あるモードにおいて、 プログラムを継続するコマンドを打つことでedebugの実行モードを指定します。 以下にそれらのコマンドの一覧を示します。 Sを除くすべてのコマンドは、 少なくともある程度プログラムの実行を再開します。

S
停止: プログラムをいっさい実行せずに、 edebugコマンドの入力を待つ(edebug-stop)。

SPC
ステップ実行: つぎに出会う停止位置で止まる(edebug-step-mode)。

n
つぎ: 式のうしろでつぎに出会う停止位置で止まる (edebug-next-mode)。 17.2.5 edebugのその他のコマンドedebug-forward-sexpも参照。

t
トレース: edebugの各停止位置で1秒間休止する (edebug-trace-mode)。

T
高速トレース: 各停止位置で表示を更新するが休止しない (edebug-Trace-fast-mode)。

g
実行: つぎのブレークポイントまで実行する (edebug-go-mode)。 see 節 17.2.6 ブレークポイント

c
継続: 各ブレークポイントで1秒間休止してから継続する (edebug-continue-mode)。

C
高速継続: 各ブレークポイントへポイントを移動するが休止しない (edebug-Continue-fast-mode)。

G
非停止実行: ブレークポイントを無視する (edebug-Go-nonstop-mode)。 Sや編集コマンドを打てば停止できる。

一般に、上記一覧の上にある実行モードほど下にあるものに比べると プログラムをゆっくり実行、つまり、早く停止します。

実行中やトレース中には、edebugコマンドをなにか打てば実行に割り込めます。 edebugはつぎの停止位置でプログラムを止め、 読者が打ったコマンドを実行します。 たとえば、実行中にtを打てば、つぎの停止位置でトレースモードに 切り替わります。 単に実行を停止するにはSを使います。

読者の関数が入力を読み取る場合、実行に割り込むつもりで打った文字を 関数が読み取ってしまうかもしれません。 読者のプログラムがいつ入力するかに注意していれば、 このような意図しない結果を避けることができます。

本節で述べたコマンドを含むキーボードマクロは動作しません。 つまり、プログラムを再開するためにedebugから抜けると キーボードマクロの制御を失ってしまいます。 これを修正するのは簡単ではありません。 また、edebugの外側でキーボードマクロを定義したり実行しても、 edebug内のコマンドにはなんの影響もありません。 これは普通は利点です。 しかし、オプションedebug-continue-kbd-macro (see 節 17.2.16 edebugのオプション)も参照してください。

edebugの新たなレベルに入ると、変数edebug-initial-modeの値を 実行モードの初期値とします。 デフォルトでは、これはステップ実行モードを指定します。 処置した関数を1つのコマンドから複数回呼び出すなどして edebugの同一レベルに再度入ることができることに注意してください。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.4 ジャンプ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Jumping"
"elisp/ジャンプ"へのコメント(無し)

本節で述べるコマンドは、指定した位置に達するまで実行します。 iを除くすべてのものは、停止する場所に一時的なブレークポイントを 設定してから実行モードに移行します。 意図したブレークポイントより先に別のブレークポイントに達しても 実行を停止します。 ブレークポイントについて詳しくはSee 節 17.2.6 ブレークポイント

非ローカル脱出は、読者が意図したプログラムの停止すべき 一時的なブレークポイントを迂回するため、 これらのコマンドは非ローカル脱出があると意図したように動作しません。

h
ポイント位置付近の停止位置まで進む(edebug-goto-here)。

f
プログラムの式1つ分先へ進む(edebug-forward-sexp)。

o
囲んでいるS式の終りまでプログラムを実行する。

i
ポイントのあとのフォームから呼ばれる関数やマクロへ進む。

コマンドhは、一時的なブレークポイントを使って、 ポイント位置付近の停止位置まで進みます。 ブレークポイントについて詳しくはSee 節 17.2.6 ブレークポイント

コマンドfは、プログラムの式1つ分先へ進みます。 より正確には、C-M-fによる移動箇所へ一時的なブレークポイントを設定し、 プログラムがブレークポイントで停止するような実行モードで実行します。

前置引数nを指定すると、 ポイント位置からn個先のS式に一時的なブレークポイントを設定します。 囲んでいるリストの残り要素数がnより少なければ、 囲んでいる式の末尾で停止します。

C-M-fの移動先はプログラムが実際に停止するであろう箇所です。 これが正しくない場合もあり、たとえば、condでは正しくありません。

コマンドfは、柔軟性のために、 停止位置ではなくポイント位置でforward-sexpを使います。 現在の停止位置から式1つだけ実行したい場合には、 まずwと打ってポイントを停止位置に移動してからfを打ちます。

コマンドoは式から『出る』まで実行します。 ポイントを含むS式の末尾に一時的なブレークポイントを置きます。 このS式が関数定義そのものである場合には、 oは定義の最後のS式の手前まで実行します。 現在この箇所にいた場合には、関数から戻ってから停止します。 いいかえれば、最後のS式のあとに位置していない限り、 このコマンドは現在実行中の関数から抜けません。

コマンドiは、 ポイント位置のあとにあるリストフォームから呼ばれる関数やマクロへ進み、 最初に出会った停止位置で止まります。 そのフォームはこれから評価されるフォームである必要はありません。 しかし、評価されるフォームが関数呼び出しである場合には、 引数を評価するまえにこのコマンドを使うことを覚えておいてください。 さもないとこのコマンドを使う時期が遅すぎます。

コマンドiは、呼び出す関数やマクロが処置されていないと それらを処置します。 これは便利ですが、それらの関数やマクロは、明示的に処置を取り除かない限り、 処置したままになります。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.5 edebugのその他のコマンド

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Misc"
"elisp/edebugのその他のコマンド"へのコメント(無し)

edebugの他のコマンドを以下に示します。

?
edebugのヘルプメッセージを表示する(edebug-help)。

C-]
1つまえのレベルのコマンドレベルへ戻る(abort-recursive-edit)。

q
エディタのトップレベルのコマンドループへ戻る(top-level)。 edebugのすべての動作中のレベルを含めて、すべての再帰編集レベルから抜ける。 しかし、フォームunwind-protectcondition-caseで保護した 処置済みのコードがあるとデバッガを再開する。

Q
qと同様であるが保護したコードでも停止しない (top-level-nonstop)。

r
もっとも最近の式の既知の結果をエコー領域に再表示する (edebug-previous-result)。

d
わかりやすいようにedebug自体の関数を除外してバックトレースを表示する (edebug-backtrace)。

edebugのバックトレースバッファでは、 標準のデバッガのようにはデバッガのコマンドを使えない。

実行を継続するとバックトレースバッファは自動的に削除される。

edebugの再帰編集から、edebugを再帰的に活性にするコマンドを起動できます。 edebugが活性であるときにはいつでもqでトップレベルへ戻るか、 C-]で1つの再帰編集レベルを抜けることができます。 保留している評価すべてのバックトレースはdで表示できます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.6 ブレークポイント

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Breakpoints"
"elisp/ブレークポイント"へのコメント(無し)

edebugのステップ実行モードは、つぎの停止位置に達すると実行を停止します。 edebugが実行を止める方法は3つあります。 ブレークポイント、グローバルブレーク条件、ソースのブレークポイントです。

edebugを使用中には、読者がテスト中のプログラムにブレークポイント (breakpoint)、つまり、実行を停止すべき箇所を設定できます。 17.2.1 edebugの使い方で定義した任意の停止位置にブレークポイントを設定できます。 ブレークポイントの設定や解除において対象となる停止位置は、 ソースコードバッファのポイント位置かそのあとにある停止位置です。 ブレークポイントに関するedebugコマンドはつぎのとおりです。

b
ポイント位置かそのうしろにある停止位置にブレークポイントを設定する (edebug-set-breakpoint)。 前置引数を指定すると、一時的なブレークポイントになる (そこでプログラムが停止すると解除される)。

u
ポイント位置かそのうしろにある停止位置の(あれば)ブレークポイントを解除する (edebug-unset-breakpoint)。

x condition RET
conditionnil以外の値に評価される場合にのみ プログラムを停止する条件付きブレークポイントを設定する (edebug-set-conditional-breakpoint)。 前置引数を指定すると、一時的なブレークポイントになる。

B
現在の定義内にあるつぎのブレークポイントにポイント位置を移動する (edebug-next-breakpoint)。

edebug内では、bでブレークポイントを設定し、 uで解除できます。 まず目的のedegugの停止位置にポイント位置を移動し、 bを打ってその箇所にブレークポイントを設定したり、 uを打ってその箇所のブレークポイントを解除します。 設定されていないブレークポイントを解除しても、なにも起こりません。

定義を再評価したり再処置すると、その中のブレークポイントすべてを解除します。

条件付きブレークポイント(conditional breakpoint)は、 プログラムがこの箇所に達するたびに条件を検査します。 条件を評価中に発生するどんなエラーも無視し、 nilとして扱います。 条件付きブレークポイントを設定するにはxを使い、 条件式はミニバッファで指定します。 すでに条件付きブレークポイントを設定してある停止位置に 条件付きブレークポイントを設定し直すと、 それまでの条件式がミニバッファに入るので編集できます。

ブレークポイントを設定するコマンドに前置引数を指定すると、 条件付き/無条件ブレークポイントを一時的なものにできます。 一時的ブレークポイントでプログラムが停止すると、 そのブレークポイントは自動的に解除されます。

edebugのモードが非停止実行でなければ、 edebugはブレークポイントでつねに停止するか休止します。 非停止実行モードでは、ブレークポイントを完全に無視します。

ブレークポイントの場所を確認するには、コマンドBを使います。 同じ関数内のポイント箇所のうしろにあるブレークポイントか、 後続のものがなければ最初のブレークポイントにポイント位置を移動します。 このコマンドは実行を継続しません。 バッファ内で単にポイントを移動するだけです。

17.2.6.1 グローバルブレーク条件    Breaking on an event.
17.2.6.2 ソース上のブレークポイント    Embedding breakpoints in source code.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.6.1 グローバルブレーク条件

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Global%20Break%20Condition"
"elisp/グローバルブレーク条件"へのコメント(無し)

グローバルブレーク条件(global break condition)は、 指定した条件が満たされると、その場所に関わらず、実行を停止させます。 edebugは各停止位置においてグローバルブレーク条件を評価します。 これがnil以外の値であると、 ブレークポイントに達したかのように、 実行モードに依存して実行を停止するか休止します。 条件の評価中にエラーが発生しても実行は停止しません。

条件式はedebug-global-break-conditionに保存されます。 コマンドXで新たな条件式を指定できます (edebug-set-global-break-condition)。

グローバルブレーク条件は、読者のコードのどこでイベントが発生するかを 調べるもっとも簡単な方法ですが、コードの実行速度をかなり遅くします。 ですから、使用しない場合には条件をnilに再設定すべきです。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.6.2 ソース上のブレークポイント

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Source%20Breakpoints"
"elisp/ソース上のブレークポイント"へのコメント(無し)

定義内のすべてのブレークポイントは、定義を処置し直すたびに失われます。 ブレークポイントを失いたくない場合には、 ソース上のブレークポイント(source breakpoint)を指定できます。 これはソースコード上で関数edebugを呼び出すだけです。 もちろん、条件付けして呼び出せます。 たとえば、関数facにおいて、引数がゼロの場合に停止するには、 以下に示すように最初の行を挿入します。

 
(defun fac (n)
  (if (= n 0) (edebug))
  (if (< 0 n)
      (* n (fac (1- n)))
    1))

関数facを処置してこの関数を呼び出すと、 edebugの呼び出しはブレークポイントのように動作します。 実行モードに応じて、edebugはその箇所で停止するか休止します。

edebugを呼び出したコードが処置済みでなければ、 この関数はdebugを呼び出します。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.7 エラーの捕捉

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Trapping%20Errors"
"elisp/エラーの捕捉"へのコメント(無し)

Emacsは、通常、エラーが通知されてもcondition-caseで処理されなかった 場合、エラーメッセージを表示します。 edebugが活性であり処置済みのコードを実行していると、 edebugは処理されなかったエラーすべてに反応します。 この動作をedebug-on-erroredebug-on-quitで カスタマイズできます。 See 節 17.2.16 edebugのオプション

edebugがエラーに反応すると、 エラーを起こすまえ出会った最後の停止位置を表示します。 この位置は、実際にエラーを起こした処置してない関数の呼び出し位置である 場合もあります。 未束縛な変数のエラーでは、最後の停止位置は、 当該変数の参照位置からかなり離れている場合があります。 そのような場合には、完全なバックトレースを表示したいでしょう (see 節 17.2.5 edebugのその他のコマンド)。

edebugが活性なときにdebug-on-errordebug-on-quitを変更しても、 edebugが不活性になったときにそれらの変更を取り消してしまいます。 さらに、edebugの再帰編集中は、これらの変数はedebugの外側での値に 束縛されます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.8 edebugのビュー

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Views"
"elisp/edebugのビュー"へのコメント(無し)

これらのedebugのコマンドは、edebugに入るまえのバッファやウィンドウの 状態を調べるものです。 外部ウィンドウ構成は、edebugの外側でのウィンドウの集まりや内容に 関するものです。

v
外部ウィンドウ構成を一時的に見る (edebug-view-outside)。

p
edebugの外側でのカレントバッファと外側でのポイント位置を 一時的に表示する(edebug-bounce-point)。 前置引数nは、かわりに休止秒数を指定する。

w
ソースコードバッファで現在の停止位置にポイント位置を戻す (edebug-where)。

同じバッファを表示している別のウィンドウでこのコマンドを使うと、 それ以後、そのウィンドウに現在の定義が表示されるようになる。

W
edebugが外部ウィンドウ構成を保存/復元するかどうかをトグルする (edebug-toggle-save-windows)。

前置引数を指定すると選択したウィンドウだけの保存/復元をトグルする。 ソースコードバッファを表示していないウィンドウを指定するには、 グローバルキーマップのC-x X Wを使う必要がある。

vで外部ウィンドウ構成を見ることができます。 あるいは、(edebugの外側での)カレントバッファが表示されていなくても pでカレントバッファのポイント位置を見ることができます。 ポイント位置を移動したら、 wでソースコードバッファの停止位置へ戻れます。

外部ウィンドウ構成を保存しないようにWを使うたびに、 edebugは保存しておいた外部ウィンドウ構成を破棄します。 そのため、保存するように戻しても、 (プログラムを続行することで)edebugを抜けると、 現在のウィンドウ構成は変更されません。 しかし、`*edebug*'と`*edebug-trace*'の自動再表示は、 十分なウィンドウが開いてないと、 読者が見たいバッファと衝突するかもしれません。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.9 評価

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Eval"
"elisp/評価"へのコメント(無し)

edebugの内側では、edebugが動作していないがごとく式を評価できます。 edebugは、式の評価と表示に対して見えないようにします。 edebugが明示的に保存/復元する場合を除いて、 副作用を持つ式の評価も期待どおり動作します。 この処理に関して詳しくはSee 節 17.2.14 外側の文脈

e exp RET
edebugの外側の文脈で式expを評価する (edebug-eval-expression)。 つまり、edebugは評価への干渉を最小限にとどめようとする。

M-: exp RET
edebug自身の文脈で式expを評価する。

C-x C-e
edebugの外側の文脈でポイント位置のまえの式を評価する (edebug-eval-last-sexp)。

edebugは`cl.el'(版2.03以降)内の構文 lexical-letmacroletsymbol-macroletで 作成されるレキシカル(テキスト上の)束縛を参照する式の評価を扱えます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.10 評価リストバッファ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Eval%20List"
"elisp/評価リストバッファ"へのコメント(無し)

`*edebug*'と呼ばれる評価リストバッファを使って、 式を対話的に評価できます。 さらに、edebugが表示を更新するたびに自動的に評価される 式の評価リストを設定することもできます。

E
評価リストバッファ`*edebug*'へ切り替える (edebug-visit-eval-list)。

バッファ`*edebug*'では、以下の特別なコマンドに加えて lisp対話モード (see 節 `lisp対話バッファ' in

GNU Emacs マニュアル
) のコマンドも使えます。

C-j
外側の文脈でポイント位置のまえの式を評価し、 その値をバッファに挿入する (edebug-eval-print-last-sexp)。

C-x C-e
edebugの外側の文脈でポイント位置のまえの式を評価する (edebug-eval-last-sexp)。

C-c C-u
バッファの内容から新たな評価リストを構築する (edebug-update-eval-list)。

C-c C-d
ポイント位置にある評価リストグループを削除する (edebug-delete-eval-item)。

C-c C-w
ソースコードバッファに切り替え現在の停止位置に戻る (edebug-where)。

`*scratch*'で行うのと同様に、 評価リストウィンドウではC-jC-x C-eで式を評価できますが、 それらはedebugの外側の文脈で評価されます。

実行を継続すると、対話的に入力した式(やその結果)は破棄されますが、 実行を停止するたびに評価される式から成る評価リスト(evaluation list) を設定できます。

これを行うには、評価リストバッファにて、 1つ以上の評価リストグループ(evaluation list group)を書きます。 評価リストグループは、1つ以上のLisp式から成ります。 グループはコメント行で区切ります。

コマンドC-c C-uedebug-update-eval-list)は、 バッファを走査して各グループの最初の式を使って 評価リストを再構築します。 (各グループの2番目の式は計算結果を表示した値とみなす。)

edebugに入るたびに、各式に続けてその現在値をバッファに挿入することで 評価リストを再表示します。 このとき、各式がそれぞれグループになるようにコメント行も挿入します。 したがって、バッファのテキストを変更せずに再度C-c C-uと打つと、 評価リストは実質的には変更されません。

評価リストの評価中にエラーが発生すると、 エラーメッセージを評価結果とみなして文字列で表示します。 したがって、現在の文脈では不正な変数を式に使っても 読者のデバッグを遮ることはありません。

評価リストウィンドウに数個の式を追加したときのようすを以下に示します。

 
(current-buffer)
#
;---------------------------------------------------------------
(selected-window)
#
;---------------------------------------------------------------
(point)
196
;---------------------------------------------------------------
bad-var
"Symbol's value as variable is void: bad-var"
;---------------------------------------------------------------
(recursion-depth)
0
;---------------------------------------------------------------
this-command
eval-last-sexp
;---------------------------------------------------------------

グループを削除するには、そこへポイントを移動してC-c C-dと打ちます。 あるいは、グループのテキストを単に削除してからC-c C-uで 評価リストを更新します。 評価リストに新たに式を追加するには、 適切な位置に式を挿入し、新たなコメント行を挿入します。 (コメント行にマイナス記号を挿入する必要はない。 コメントの内容は関係ない。) そして、C-c C-uと打ちます

`*edebug*'を選択したあとは、 C-c C-wでソースコードバッファへ戻れます。 読者が実行を継続するとバッファ`*edebug*'は削除され、 つぎに必要なときに再度作成されます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.11 edebugでの出力

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Printing%20in%20Edebug"
"elisp/edebugでの出力"へのコメント(無し)

読者のプログラムの式が循環したリスト構造を含む値を作り出す場合、 edebugがそれを出力しようとするとエラーになります。

循環構造を扱う1つの方法は、出力を切り詰めるために print-lengthprint-levelを設定することです。 edebugが読者のためにこれを行います。 それらがnilであると、 edebugはprint-lengthprint-levelを50に束縛します。 (実際は、edebugが使う値は e-debug-print-lengthe-debug-print-levelが指定する。) See 節 18.6 出力に影響する変数

User Option: edebug-print-length
nil以外であると、edebugが結果を出力するときには、 これをprint-lengthに束縛する。 デフォルト値は50

User Option: edebug-print-level
nil以外であると、edebugが結果を出力するときには、 これをprint-levelに束縛する。 デフォルト値は50

パッケージ`cust-print'を使えば、 循環構造や要素を共有する構造をより的確に出力することもできます。

`cust-print'をロードしてedebugでのみこの特別な出力を使うようにするには、 単にコマンドM-x edebug-install-custom-printを使うだけです。 標準の出力関数に戻すには、M-x edebug-uninstall-custom-printを使います。

循環構造を作るコードの例を示します。

 
(setq a '(x y))
(setcar a a)

特別な出力ではこれを`Result: #1=(#1# y)'と出力します。 `#1='の記法は、これに続く構造に`1'というラベルを付けます。 また、`#1#'の記法はすでにラベル付けした構造を参照します。 この記法は、リストやベクトルの任意の共有された要素に使われます。

User Option: edebug-print-circle
nil以外であると、edebugが結果を出力するときには、 これをprint-circleに束縛する。 デフォルト値はnil

他のプログラムでもこの特別な出力を使えます。 詳しくは、`cust-print.el'を参照してください。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.12 トレースバッファ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Trace%20Buffer"
"elisp/トレースバッファ"へのコメント(無し)

edebugは、実行トレースを`*edebug-trace*'というバッファに保存することで それらを記録できます。 これは、関数名とそれらの引数、戻り値から成る 関数呼び出しとその戻りの記録です。 トレース記録を有効にするには、 edebug-tracenil以外の値を設定します。

トレースバッファを作成することとトレース実行モードとは 同じではありません(see 節 17.2.3 edebugの実行モード)。

トレース記録を有効にしていると、 各関数へ入るときと出るときに、トレースバッファに行が追加されます。 関数へ入るときの記録は、`::::{'に関数名と引数値が続きます。 関数から出るときの記録は、`::::}'に関数名とその結果が続きます。

入るときの`:'の個数は、再帰の深さを表します。 関数呼び出しの対応する開始や対応する終了を探すために トレースバッファでは中括弧を使えます。

関数edebug-print-trace-beforeedebug-print-trace-afterを 再定義すれば、関数へ入ったときと出るときのトレース記録をカスタマイズできます。

Macro: edebug-tracing string body...
このマクロはフォームbodyの周りにトレース情報を追加する。 引数stringは、トレースバッファに入れるテキストを指定する。 すべての引数を評価する。 edebug-tracingbodyの最後のフォームの値を返す。

Function: edebug-trace format-string &rest format-args
この関数はトレースバッファにテキストを挿入する。 テキストは(apply 'format format-string format-args)で 計算する。 区切りとして改行も挿入する。

edebug-tracingedebug-traceは、 edebugが活性でない場合であっても呼ばれるとトレースバッファに行を挿入します。 トレースバッファにテキストを挿入するとき、 挿入した最後の行が見えるようにウィンドウをスクロールします。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.13 カバレッジテスト

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Coverage%20Testing"
"elisp/カバレッジテスト"へのコメント(無し)

edebugでは、初歩的なカバレッジテストや実行頻度を表示できます。

カバレッジテストでは、各式の結果を以前の結果と比較します。 現在のEmacsセッションでカバレッジテストを始めて以降、 プログラムの各フォームが異なる2つの値を返せば、 当該フォームを『カバーした』とみなします。 したがって、読者のプログラムについてカバレッジテストを行うには、 さまざまな条件でそれを実行して正しく動作しているか注意します。 読者が各フォームが異なる2つの値を返すように試行し終れば、 edebugはそのように通知します。

カバレッジテストは実行速度を遅くするので、 edebug-test-coveragenil以外の場合にのみテストします。 すべての処置済み関数の実行に関する頻度数計測は、 非停止実行モードであってもカバレッジテストのオン/オフに関わらず行います。

ある定義に関するカバレッジテストと頻度数計測を表示するには M-x edebug-display-freq-countを使います。

コマンド: edebug-display-freq-count
このコマンドは、現在の定義の各行について頻度数データを表示する。

頻度数は、コードの各行のあとにコメント行として表示され、 コマンドundoでそれらのコメント行の挿入をアンドゥできる。 頻度数は、式のまえの`('や式のうしろの`)'の直下、 あるいは、変数の最後の文字に表示される。 表示を簡素にするために、頻度数が同じ行のまえのほうの式の頻度数と同じであると 表示しない。

式の頻度数に続く文字`='は、 その式を評価するたびに同じ値を返したことを意味する。 いいかえれば、カバレッジテストとしては、 その式はまだ『カバーして』いないことになる。

ある定義に関する頻度数計測とカバレッジデータをクリアするには、 eval-defunで単に再処置すればよい。

たとえば、edebug-test-coveragetとし、 ソース上のブレークポイントを設定して(fac 5)を評価すると、 ブレークポイントに達したときの頻度数データはつぎのようになります。

 
(defun fac (n)
  (if (= n 0) (edebug))
;#6           1      0 =5 
  (if (< 0 n)
;#5         = 
      (* n (fac (1- n)))
;#    5               0  
    1))
;#   0 

コメント行は、facが6回呼ばれたことを表します。 最初のif文は、5回とも同じ結果を返したのです。 2番目のifについても同じです。 facの再帰呼び出しは1度も戻っていません。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.14 外側の文脈

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=The%20Outside%20Context"
"elisp/外側の文脈"へのコメント(無し)

edebugは、読者がデバッグ中のプログラムに対しては透過であるように努めますが、 完全にうまくいくとは限りません。 また、eで読者が式を評価するときや評価リストバッファでも、 外側の文脈を一時的に復元して透過であるように努めます。 本節では、edebugが復元する文脈について正確に説明し、 edebugが透過にならない場合についても説明します。

17.2.14.1 停止すべきかどうかの検査    When Edebug decides what to do.
17.2.14.2 edebugの表示の更新    When Edebug updates the display.
17.2.14.3 edebugの再帰編集    When Edebug stops execution.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.14.1 停止すべきかどうかの検査

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Checking%20Whether%20to%20Stop"
"elisp/停止すべきかどうかの検査"へのコメント(無し)

edebugに入ると、トレース情報を作るのかプログラムを停止するのかを 決定するまえであってもある種のデータを保存/復元する必要が つねにあります。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.14.2 edebugの表示の更新

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Display%20Update"
"elisp/edebugの表示の更新"へのコメント(無し)

edebugが(トレースモードなどで)なにかを表示する必要があると、 edebugの『外側』の現在のウィンドウ構成を保存します (see 節 27.16 ウィンドウ構成)。 (プログラムを続行して)edebugを抜けるときには、 以前のウィンドウ構成を復元します

Emacsは休止するときにのみ表示を更新します。 通常、プログラムを続行しても、休止したり入力を読むことなく ブレークポイントやステップ実行によりedebugへ戻ります。 そのような場合、Emacsには(edebugの)『外側』のようすを再表示する 機会が与えられません。 見かけ上、ウィンドウの表示は直前にedebugが活性であったときと同じになります。

なにかを表示するためにedebugに入っても以下のデータを保存/復元しますが、 エラーや中断が起こると、故意に復元しないものもあります。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.14.3 edebugの再帰編集

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Recursive%20Edit"
"elisp/edebugの再帰編集"へのコメント(無し)

edebugに入ってユーザーコマンドを読み取るとき、 以下のデータを保存し(のちに復元し)ます。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.15 マクロ呼び出しの処置

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Instrumenting%20Macro%20Calls"
"elisp/マクロ呼び出しの処置"へのコメント(無し)

edebugがLispマクロを呼び出す式を処置するとき、 それを正しく行うにはマクロに関する余分な情報を必要とします。 マクロ呼び出しのどの部分式が評価されるフォームであるかを 明確に判定する方法がないからです。 (マクロ本体で明示的に評価されるか、 結果の展開形が評価されるときか、あるいは、さらにあと)

したがって、edebugが出会う各マクロについて、 当該マクロの呼び出し形式を記述するedebug用仕様を定義する必要があります。 これには、def-edebug-specを使います。

マクロ: def-edebug-spec macro specification
マクロmacroの呼び出しのどの式が評価されるかを指定する。 単純なマクロでは、specificationは マクロ定義の仮引数リストに似ているが、 その指定はマクロ引数よりも汎用性がある。

引数macroはマクロ名だけでなく任意のシンボルでよい。

例題マクロfor(see 節 12.6.1 マクロ引数の複数回評価)の edebug用仕様の等価な定義例2つを示します。

 
(def-edebug-spec for
  (symbolp "from" form "to" form "do" &rest form))

(def-edebug-spec for
  (symbolp ['from form] ['to form] ['do body]))

specificationに指定するものとその引数の処理方法は次表のとおりです。

t
すべての引数を評価するように処置する。

0
引数はいっさい処置しない。

シンボル
edebug用仕様を持つシンボルをかわりに使う。 この間接参照は別の種類の仕様を得るまで繰り返す。 これにより、別のマクロから仕様を継承できる。

リスト
リストの各要素は、呼び出しフォームの引数の型を記述する。 仕様リストの各要素については次節で述べる。

17.2.15.1 仕様リスト    How to specify complex patterns of evaluation.
17.2.15.2 仕様内でのバックトラック    What Edebug does when matching fails.
17.2.15.3 仕様の例    To help understand specifications.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.15.1 仕様リスト

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Specification%20List"
"elisp/仕様リスト"へのコメント(無し)

マクロ呼び出しの引数のあるものは評価し別のものは評価しない場合には、 edebug用仕様に仕様リスト(specification list)が必要になります。 複数の引数に一致する仕様リストの要素もありますが、 後続の要素の処理を修飾する要素もあります。 後者は仕様キーワード(specification keyword)と呼ばれ、 (&optionalのように)`&'で始まるシンボルです。

仕様リストには、それ自体がリストである引数に一致する部分リストや グループ化に使うベクトルを含んでもかまいません。 部分リストやグループは仕様リストを階層に分けます。 仕様キーワードはそれらを含む部分リストやグループの残りに適用されます。

仕様リストに選択肢や繰り返しが含まれる場合、 実際のマクロ呼び出しに一致させるにはバックトラックが必要な場合もあります。 詳しくはSee 節 17.2.15.2 仕様内でのバックトラック

edebug用仕様では、正規表現による一致と文脈自由文法の構文を使えます。 対応した括弧に囲まれた部分リスト、フォームの再帰的処理、 間接仕様による再帰です。

仕様リストの要素に指定できるものとそれらの意味を以下に示します。

sexp
処置しない評価しない1つのLispオブジェクト。

form
処置した評価する1つの式。

place
Common Lispのsetf構文のように値を格納する場所。

body
&rest formの省略形。 以下の&restを参照。

function-form
関数フォーム。 クォートした関数シンボル、クォートしたラムダ式、 あるいは、(関数シンボルやラムダ式に評価される)フォーム。 これは、ラムダ式である引数が、 functionではなくquoteでクォートされるときに有用である。 というのは、ラムダ式の本体をいずれかの方法で処置するからである。

lambda-expr
クォートしていないラムダ式。

&optional
この仕様リスト内の後続の要素すべては省略可能。 一致しないと、edebugはただちにこのレベルの一致を止める。

数個の省略可能な要素に省略不可な要素を続けるには、 [&optional specs...]を使う。 数個の要素がすべて一致するかまったく一致しないことを指定するには、 &optional [specs...]を使う。 以下のdefunの例を参照。

&rest
この仕様リスト内の後続の要素すべてを0回以上繰り返す。 最後の繰り返しでは、仕様リストの要素すべてに一致するまえに 式を使い尽くしても問題にはならない。

数個の要素のみを繰り返すには[&rest specs...]を使う。 各繰り返しですべてが一致するような数個の要素を指定するには、 &rest [specs...]を使う。

&or
仕様リスト内の後続の各リストは選択肢を表す。 選択肢の1つに一致しなければ、仕様&orは失敗。

&orに続く各要素は1つの選択肢を表す。 複数の要素を1つの選択肢としてグループにまとめるには、 それらを[...]で囲む。

¬
&orを使ったかように後続の要素を選択肢として一致させるが、 どれかが一致すると仕様は失敗。 どれにも一致しなければ、仕様¬は成功。

&define
仕様は定義フォームに対するものであることを表す。 定義フォームそのものは処置しない (つまり、edbugは定義フォームの前後で停止しない)が、 この内側にあるフォームは典型的には処置される。 キーワード&defineはリスト仕様の最初の要素である必要がある。

nil
現在の引数リストのレベルにおいて一致する引数がなければ成功する。 さもなければ失敗。 部分リスト仕様と以下のバッククォートの例を参照。

gate
いかなる引数とも一致しないが、このゲートを通ると、 このレベルの仕様の残りの部分と一致を調べる際には バックトラックを禁止する。 これは主により詳しい構文エラーメッセージを生成するために使う。 詳しくは17.2.15.2 仕様内でのバックトラックを参照。 例については以下のletを参照。

その他のシンボル
仕様リストのその他のシンボルは述語であるか間接仕様である。

シンボルにedebug用仕様があれば、 この間接仕様は、シンボルのかわりに使われる仕様リストであるか、 引数を処理するために呼び出される関数であること。 仕様は、マクロ向けにdef-edebug-specで定義した仕様であってもよい。 以下のdefunの例を参照。

さもなければ、シンボルは述語であること。 述語は引数で呼び出され、述語がnilを返すと仕様は失敗する。 いずれの場合でも、当該引数は処置されない。

適当な述語には、symbolpintegerpstringpvectorpatomがある。

[elements...]
要素のベクトルは要素群を単一のグループ仕様にまとめる。 この意味はベクトルの意味とは関係ない。

"string"
引数はstringという名前のシンボルであること。 この仕様は、symbolの名前がstringである クォートとしたシンボル'symbolと等価であるが、 文字列のほうが望ましい。

(vector elements...)
引数は、仕様内のelementsに一致するものを要素とするベクトルであること。 以下のバッククォートの例を参照。

(elements...)
その他のリストは部分リスト仕様であり、 引数は仕様elementsに一致するものを要素とするリストであること。

部分リスト仕様はドット対リストでもよく、その場合、 対応するリスト引数はドット対リストである。 あるいは、ドット対リスト仕様の最後のCDRは ((spec . [(more specs...)])などの グループや間接仕様を介した) 別の部分リスト仕様であってもよいが、 それらの要素はドット対ではないリスト引数に一致する。 これは、以下のバッククォートの例のような再帰仕様に有用である。 このような再帰を終らせるうえの仕様nilも参照。

(specs . nil)(specs . (sublist-elements...))のような部分リスト仕様は (specs sublist-elements...)と等価であることに注意。

&defineのうしろに追加できる仕様の一覧を以下に示します。 以下のdefunの例を参照してください。

name
引数はシンボルであり、定義フォームの名前である。

定義フォームには単一の名前フィールドがある必要はなく、 複数の名前フィールドを持っていてもよい。

:name
この構造は引数には実際には一致しない。 :nameに続く要素はシンボルであること。 定義に対する追加の名前要素として使う。 定義の名前に一意で静的な要素を追加するために使う。 複数あってもよい。

arg
引数はシンボルであり、定義フォームの引数の名前である。 しかし、ラムダリストキーワード(`&'で始まるシンボル)は許されない。

lambda-list
ラムダリスト、すなわち、ラムダ式の引数リストに一致する。

def-body
引数は、定義内のコード本体である。 これは、上に述べたbodyに似ているが、 定義本体は定義に関連した情報を調べる異なるedebug呼び出しで処置する必要がある。 定義内のフォームの最上位レベルのリストにはdef-bodyを使う。

def-form
引数は、定義内の単一の最上位レベルのフォームである。 これはdef-bodyに似ているが、 フォームのリストではなく単一のフォームに一致するものに使う。 特別な場合として、def-formは フォームを実行したときにトレース情報を出力しないことを意味する。 以下のinteractiveの例を参照。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.15.2 仕様内でのバックトラック

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Backtracking"
"elisp/仕様内でのバックトラック"へのコメント(無し)

仕様の一致がある箇所で失敗しても、 必ずしも構文エラーが通知されるとは限りません。 そのかわりに、選択肢すべてを試し尽くすまでバックトラックします。 最終的に、引数リストの各要素は仕様内のいずれかの要素に一致する必要があり、 仕様内の各必須要素はいずれかの引数に一致する必要があります。 構文エラーを検出しても、より高いレベルの選択肢を使い切るまでは報告されず、 実際のエラー箇所から離れた箇所にポイントが置かれます。 しかし、エラー発生時にバックトラックが禁止されていれば、 ただちにエラーが報告されます。 さまざまな状況でバックトラックが自動的に再許可されることに注意してください。 &optional&rest&orで新たに選択肢が指定されたり、 部分リストやグループや間接仕様を処理し始めると、 自動的に再許可されます。 バックトラックの許可/禁止の効果は、 現在処理しているレベルやそれより低いレベルに限定されます。

任意のフォーム仕様(つまり、formbodydef-formdef-body)の一致処理中には、バックトラックを禁止します。 これらの仕様は任意のフォームに一致するので、 エラーはより上のレベルではなくフォーム自身にあるはずです。

また、クォートしたシンボルや文字列の仕様に一致すると バックトラックを禁止します。 というのは、通常、これは構造を認識したことを意味するからです。 しかし、すべてが同一シンボルで始まる選択肢を指定する場合には、 ["foo" &or [first case] [second case] ...]のように、 そのシンボルを選択肢から括り出せばバックトラックするようにできます。

多くの場合では、バックトラックを自動的に禁止するこれらの2つの方法で十分ですが、 仕様gateを使ってバックトラックを明示的に禁止すると有用な場合もあります。 上位の選択肢が適用できないとわかっている場合に有用です。 仕様letの例を参照してください。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.15.3 仕様の例

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Specification%20Examples"
"elisp/仕様の例"へのコメント(無し)

以下の例を参考にするとedebug用仕様を理解しやすいでしょう。

スペシャルフォームletには束縛と本体の並びがあります。 各束縛は、シンボル、あるいは、シンボルと省略可能な式から成る部分リストです。 以下のedebug用仕様では、部分リストの内側にあるgateで、 部分リストを一度みつけるとバックトラックを禁止していることに 注意してください。

 
(def-edebug-spec let
  ((&rest
    &or symbolp (gate symbolp &optional form))
   body))

edebugは、defundefmacro、 および、対応する引数リストと仕様interactiveに対しては、 以下のedebug用仕様を使います。 式の引数は実際には関数本体の外側で評価されるので、 対話宣言フォームを特別扱いする必要があります。

 
(def-edebug-spec defmacro defun) ; 仕様defunの間接参照
(def-edebug-spec defun 
  (&define name lambda-list 
           [&optional stringp]   ; あれば、説明文字列に一致する
           [&optional ("interactive" interactive)]
           def-body))

(def-edebug-spec lambda-list
  (([&rest arg]
    [&optional ["&optional" arg &rest arg]]
    &optional ["&rest" arg]
    )))

(def-edebug-spec interactive
  (&optional &or stringp def-form))    ; def-formを参照

以下のバッククォートに対する仕様は、 ドット対リストの一致の仕方と再帰を終らせるnilの使い方を示します。 また、ベクトルの要素の一致の仕方も示します。 (edebugが実際に定義している仕様では、 失敗の可能性がある非常に深い再帰をもたらすためドット対リストを扱わない。)

 
(def-edebug-spec ` (backquote-form))   ; わかりやすいように別名を付ける

(def-edebug-spec backquote-form
  (&or ([&or "," ",@"] &or ("quote" backquote-form) form)
       (backquote-form . [&or nil backquote-form])
       (vector &rest backquote-form)
       sexp))



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.2.16 edebugのオプション

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Edebug%20Options"
"elisp/edebugのオプション"へのコメント(無し)

以下のオプションはedebugの動作に影響します。

User Option: edebug-setup-hook
edebugを使うまえに呼び出す関数群。 新たな値に設定されるたびに、edebugはこれらの関数を呼び出し、 そのあとでedebug-setup-hooknilに再設定する。 これを用いて、edebugを使用する場合にのみ、 使用するパッケージに対応するedebug用仕様をロードできる。 see 節 17.2.2 edebug向けの処置

User Option: edebug-all-defs
これがnil以外であると、 defundefmacroのような定義フォームを普通に評価すると、 edebug用にそれらを処置する。 これは、eval-defuneval-regioneval-buffereval-current-bufferにも適用される。

このオプションの値をトグルするにはコマンドM-x edebug-all-defsを使う。 see 節 17.2.2 edebug向けの処置

User Option: edebug-all-forms
これがcode{nil}以外であると、コマンドeval-defuneval-regioneval-buffereval-current-bufferは、 定義しないフォームの場合であってもすべてのフォームを処置する。 これは、ロードやミニバッファでの評価には適用されない。

このオプションの値をトグルするにはコマンドM-x edebug-all-formsを使う。 see 節 17.2.2 edebug向けの処置

User Option: edebug-save-windows
これがnil以外であると、edebugはウィンドウ構成を保存/復元する。 これには時間がかかるので、読者のプログラムがウィンドウ構成に 依存しないのならば、この変数はnilに設定しておくほうがよい。

値がリストであると、リスト内のウィンドウのみを保存/復元する。

edebugのコマンドWを使ってこの変数を対話的に変更できる。 see 節 17.2.14.2 edebugの表示の更新

User Option: edebug-save-displayed-buffer-points
これがnil以外であると、edebugは表示されているすべてのバッファの ポイントを保存/復元する。

選択していないウィンドウに表示されたバッファのポイントを変更する コードをデバッグ中には、別のバッファのポイントを保存/復元する必要がある。 edebugやユーザーが当該ウィンドウを選択すると、 そのバッファのポイントはウィンドウのポイント位置に移動する。

すべてのバッファでポイントを保存/復元するには 各ウィンドウを2度選択する必要があるため手間がかかる。 そのため、必要な場合にのみこの機能を有効にする。 see 節 17.2.14.2 edebugの表示の更新

User Option: edebug-initial-mode
この変数がnil以外であれば、 edebugが初めて動作するときの初期の動作モードを指定する。 可能な値は、 stepnextgoGo-nonstop, traceTrace-fastcontinueContinue-fast

デフォルト値はstep。 see 節 17.2.3 edebugの実行モード

User Option: edebug-trace
nil以外であると、関数へ入るときと出るときのトレースを表示することを 意味する。 トレース出力は、`*edebug-trace*'という名前のバッファに、 関数へ入るときと出るときを各行に再帰の深さで字下げして表示する。

デフォルト値はnil

17.2.12 トレースバッファedebug-tracingも参照。

User Option: edebug-test-coverage
nil以外であれば、edebugはデバッグ対象のすべての式のカバレッジ テストを行う。 see 節 17.2.13 カバレッジテスト

User Option: edebug-continue-kbd-macro
nil以外であれば、 edebugの外側で実行するキーボードマクロを定義したり実行する。 デバッグしないので注意してこのオプションを使うこと。

User Option: edebug-on-error
debug-on-errorの以前の値がnilであると、 edebugはdebug-on-errorにこの値を束縛する。 see 節 17.2.7 エラーの捕捉

User Option: edebug-on-quit
debug-on-quitの以前の値がnilであると、 edebugはdebug-on-quitにこの値を束縛する。 see 節 17.2.7 エラーの捕捉

edebugが動作中にedebug-on-erroredebug-on-quitの値を 変更しても、新たなコマンドでedebugをつぎに起動するまでは これらの値は使用されない。

User Option: edebug-global-break-condition
nil以外であると、各停止位置で検査される式である。 結果がnil以外であるとブレークする。 エラーは無視する。 see 節 17.2.6.1 グローバルブレーク条件



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.3 不正なLisp構文のデバッグ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Syntax%20Errors"
"elisp/不正なLisp構文のデバッグ"へのコメント(無し)

Lispリーダは不正な構文を報告しますが、どこに問題があるかは報告できません。 たとえば、式を評価中のエラー『End of file during parsing』 (構文解析中にファイルの終り)は、開き括弧(あるいは開き角括弧)が 多すぎることを表します。 Lispリーダは括弧が対応していないことをファイルの末尾で検出しますが、 どこに閉じ括弧があるべきかは判断できません。 同様に、『Invalid read syntax: ")"』(不正な構文:")")は 閉じ括弧が多すぎるか開き括弧が足りないことを表しますが、 どこに括弧が足りないかは判断できません。 それでは、どこを変更すべきかどのように調べるのでしょう?

問題が単純な括弧の非対応でなければ、 各関数定義の先頭でC-M-eを試し、 関数定義の末尾に移動するかどうかをみるのは有用な技法です。 正しく移動しなければ、その関数に問題があります。

Lispによくある構文エラーは括弧の非対応なので、 これらの場面について詳しい助言を述べておきます。 (さらに、対応括弧表示モードをオンにしてポイントを移動すると非対応を 探しやすい。)

17.3.1 開き括弧の過剰    How to find a spurious open paren or missing close.
17.3.2 閉じ括弧の過剰    How to find a spurious close paren or missing open.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.3.1 開き括弧の過剰

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Excess%20Open"
"elisp/開き括弧の過剰"へのコメント(無し)

最初の手順は、括弧が対応していない関数定義を探すことです。 開き括弧が過剰であれば、ファイルの末尾に閉じ括弧を挿入し C-M-bbackward-sexp)を打ちます。 こうすると、括弧が対応していない関数定義の先頭へ移動します。 (そうしたら、C-SPC C-_ C-u C-SPCと打って、 当該箇所にマークを設定してから閉じ括弧の挿入を取り消し、 最終的にマークへ戻る。)

つぎの手順は、なにが悪いか正確に判断することです。 プログラムを調べる以外にこれを確実に行う方法はありませんが、 しばしば、既存の字下げが括弧のありかたを予想する鍵になります。 これを利用するもっとも簡単な方法はC-M-qで字下げし直し、 どのようになるか見ることです。 まだやらないでください! まず読み進んてください。

これを行うまえに、関数定義に充分な数の閉じ括弧があることを確認してください。 さもないと、C-M-qがエラーになったり、 ファイルの末尾までを字下げし直してしまいます。 ですから、関数定義の末尾へ移動して閉じ括弧を挿入しておきます。 C-M-eを使って移動しないでください。 というのは、関数定義の括弧の対応が取れていないと失敗するからです。

関数定義の先頭へ移動してC-M-qを打ちます。 通常、ある場所から関数の末尾までの行が右へずれます。 その場所の近くで、閉じ括弧が足りなかったり開き括弧が多すぎるのです。 (しかし、これが正しいと仮定してはならない。 コードを調べて確認すること。) 不具合箇所がみつかったならば、 意図した括弧に対しては古い字下げが適しているでしょうから C-_C-M-qをアンドゥします。

問題を解決できたと思ったら、再度C-M-qを使います。 古い字下げが意図した括弧の入れ子に対応していて、 必要な括弧を挿入できているならば、 C-M-qはなにも変えないはずです。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.3.2 閉じ括弧の過剰

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Excess%20Close"
"elisp/閉じ括弧の過剰"へのコメント(無し)

過剰な閉じ括弧に対処するには、まず、ファイルの先頭に開き括弧を挿入し、 その括弧のまえでC-M-fを打って、 括弧が対応していない関数定義の末尾を探します。 (そして、C-SPC C-_ C-u C-SPCと打って、 当該箇所にマークを設定して開き括弧の挿入をアンドゥし、 最終的にマークへ戻る。)

その関数定義の先頭でC-M-fと打って、 実際に対応している閉じ括弧を探します。 これにより、関数定義が終るべき場所より手前の箇所に移動するはずです。 この付近に余分な閉じ括弧がみつかることもあるでしょう。

その場所に問題がなければ、つぎにすべきことは、 関数定義の先頭でC-M-qと打つことです。 ある範囲の行が左にずれるでしょう。 もしそうならば、開き括弧が足りないか余分な閉じ括弧は、 そのような行の先頭付近にあるでしょう。 (しかし、これが正しいと仮定してはならない。 コードを調べて確認すること。) 不具合箇所がみつかったならば、 意図した括弧に対しては古い字下げが適しているでしょうから C-_C-M-qをアンドゥします。

問題を解決できたと思ったら、再度C-M-qを使います。 古い字下げが意図した括弧の入れ子に対応していて、 必要な括弧を挿入できているならば、 C-M-qはなにも変えないはずです。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

17.4 コンパイル時の問題のデバッグ

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=elisp&node=Compilation%20Errors"
"elisp/コンパイル時の問題のデバッグ"へのコメント(無し)

バイトコンパイル時にエラーが発生したときは、 通常、読者がコンパイルしているプログラムの不正な構文に原因があります。 コンパイラはバッファ`*Compile-Log*'に適切なエラーメッセージを 表示してから停止します。 メッセージにはエラーとなった関数の名前があったりなかったりします。 いずれにしても、つぎのようにしてファイルのどこでエラーが生じたかを調べます。

まず、バッファ` *Compiler Input*'に切り替えます。 (バッファ名が空白で始まり、そのため、 M-x list-buffersでは表示されないことに注意。) このバッファにはコンパイルしたプログラムが入っていて、 ポイント位置はバイトコンパイラがどこまで読み取ったかを表します。

エラーの原因が不正なLisp構文であるならば、 ポイント位置が不正構文を検出した箇所を正確に表します。 エラー原因が近くにあるとは限りません! エラーを探すために前節の方法を使ってください。

正しく読み取ったフォームのコンパイル中にエラーを検出したときには、 ポイントはそのフォームの末尾に位置しています。 この場合、この方法ではエラー箇所を正確に判別できませんが、 どの関数を確認すべきかを示しています。


[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端] [?]