11

これはマクロに関するより理論的な質問です (私はそう思います)。マクロはソース コードを取得し、それを評価せずにオブジェクト コードを生成し、プログラマーがより用途の広い構文構造を作成できるようにします。この 2 つのマクロ システムを分類する必要があるとすれば、「C スタイル」のマクロと「Lisp スタイル」のマクロがあると言えます。

実行時に実際に実行されているコードがソースと異なるため、マクロのデバッグは少し難しいようです。

デバッガーは、前処理されたソース コードに関してプログラムの実行をどのように追跡しますか? マクロに関する追加データを取得するために設定する必要がある特別な「デバッグ モード」はありますか?

C では、デバッグ用にコンパイル時のスイッチを設定することは理解できますが、Lisp の一部の形式などのインタープリター言語ではどのようにそれを行うのでしょうか?

これを試していないことをお詫びしますが、Lisp ツールチェーンを理解するには、私が費やさなければならないよりも多くの時間が必要です。

4

6 に答える 6

4

「C スタイル」マクロと「Lisp スタイル」マクロのコンパイル方法に根本的な違いがあるとは思いません。どちらも、適切なコンパイラがソースを認識する前にソースを変換します。大きな違いは、C のマクロは C プリプロセッサ (主に単純な文字列置換用の弱い二次言語) を使用するのに対し、Lisp のマクロは Lisp 自体で書かれている (したがって、何でもできる) ことです。

(余談ですが、私はコンパイルされていない Lisp をしばらく見たことがありません...確かに世紀の変わり目以降ではありません。しかしどちらかといえば、解釈されることはマクロのデバッグ問題を難しくするのではなく、簡単にするように思われます。詳しい情報があります。)

Michael に同意します。マクロを処理する C 用のデバッガーはまったく見たことがありません。マクロを使用するコードは、何かが起こる前に変換されます。Cコードをコンパイルするための「デバッグ」モードは、通常、関数、型、変数、ファイル名などを保存することを意味します-マクロに関する情報を保存するものはないと思います。

  • マクロを使用するプログラムをデバッグする場合、Lisp はここでは C とほとんど同じです: デバッガーは、マクロ アプリケーションではなく、コンパイルされたコードを認識します。通常、マクロは単純に保たれ、C と同様に、この必要を避けるために使用前に個別にデバッグされます。

  • マクロ 自体をデバッグするために、どこかで使用する前に、Lisp には C よりもこれを簡単にする機能があり macroexpand-1ます。 )。マクロ展開の前後は、マクロ展開を書いたときにエディタで直接見ることができます。

マクロ定義自体へのデバッグが役立つ状況に出くわしたときのことを思い出せません。それはマクロ定義のバグであり、その場合macroexpand-1は問題をすぐに切り分けます。または、それより下のバグである場合、通常のデバッグ機能は正常に機能し、コール スタックの 2 つのフレーム間でマクロ展開が発生したかどうかは気にしません。 .

于 2010-07-09T18:02:35.507 に答える
3

LispWorksでは、開発者はStepperツールを使用できます。

LispWorksは、完全なマクロ展開プロセスをステップスルーできるステッパーを提供します。

于 2010-07-09T21:26:34.607 に答える
2

Racketがマクロを使用してコードをデバッグするためのサポートの種類を検討する必要があります。Ken が言及しているように、このサポートには 2 つの側面があります。一方では、マクロのデバッグの問題があります。Common Lisp では、これを行う最善の方法は、マクロ フォームを手動で展開することです。CPP の場合も状況は似ていますが、より原始的です。CPP 展開だけでコードを実行し、結果を調べます。しかし、これらはどちらもより複雑なマクロには不十分であり、これがRacket にマクロ デバッガーを用意する動機となりました。これは、バインドされた識別子などの追加の GUI ベースの指示と共に、構文展開の手順を 1 つずつ示します。

マクロを使用するという点では、Racket は常に他の Scheme や Lisp の実装よりも進んでいます。各式 (構文オブジェクトとして) は、ソースの場所を含むコードと追加データであるという考え方です。このように、フォームがマクロの場合、マクロに由来する部分を含む展開されたコードは、正しいソースの場所 (フォームが実際には存在しない場所) からではなく、マクロの定義から取得されます。dmitry-vk が述べたように、Scheme と Lisp の実装の中には、サブフォームの ID を使用してこれを限定的に実装するものがあります。

于 2010-07-09T18:18:50.267 に答える
1

通常、C ソースレベルのデバッグには、行の粒度 ("next" コマンド) または命令レベルの粒度 ("step into") があります。マクロ プロセッサは、処理されたソースに特別なディレクティブを挿入し、コンパイラがコンパイルされた CPU 命令のシーケンスをソース コード行にマップできるようにします。

Lisp では、マクロとコンパイラの間に、ソース コードからコンパイル済みコードへのマッピングを追跡するための規則が存在しないため、ソース コードでシングル ステップを実行できるとは限りません。

明らかなオプションは、マクロ展開されたコードでシングル ステップを実行することです。コンパイラは、コードの最終的な拡張バージョンをすでに認識しており、ソース コードからマシン コードへのマッピングを追跡できます。

他のオプションは、操作中の Lisp 式が同一性を持つという事実を利用することです。マクロが単純で、コードを分解してテンプレートに貼り付けるだけの場合、展開されたコードの一部の式は、ソース コードから読み取られた式と (EQ 比較に関して) 同一になります。この場合、コンパイラはいくつかの式を展開されたコードからソース コードにマップできます。

于 2010-07-09T18:18:46.580 に答える
1

Lisp マクロ (おそらく C マクロとはかなり異なると思われます) やデバッグについては知りませんが、多くの (おそらくほとんどの) C/C++ デバッガは、C プリプロセッサ マクロのソースレベルのデバッグを特にうまく処理しません。

一般に、C/C++ デバッガーはマクロ定義に「ステップ」しません。マクロが複数のステートメントに展開される場合、デバッガーは通常、デバッガーの「ステップ」操作ごとに同じソース行 (マクロが呼び出される場所) にとどまります。

これにより、マクロのデバッグが他の方法よりも少し面倒になる可能性があります。これは、C/C++ でマクロを避けるもう 1 つの理由です。マクロがまったく不可解な方法で誤動作している場合は、アセンブリ モードに落としてデバッグするか、マクロを展開します (手動またはコンパイラのスイッチを使用)。そこまで極端に行かなければならないことはめったにありません。それほど複雑なマクロを作成している場合は、おそらく間違ったアプローチをとっています。

于 2010-07-09T17:26:38.957 に答える
0

簡単な答えは、複雑だということです ;-) プログラムをデバッグできるようにするためには、いくつかの異なることがあり、マクロを追跡するためにはさらに多くのことがあります。

C および C++ では、プリプロセッサを使用して、マクロとインクルードを実際のソース コードに展開します。元のファイル名と行番号は、#line ディレクティブを使用して、この展開されたソース ファイルで追跡されます。

http://msdn.microsoft.com/en-us/library/b5w2czay(VS.80).aspx

デバッグを有効にして C または C++ プログラムをコンパイルすると、アセンブラは、ソース行、シンボル名、型記述子などを追跡する追加情報をオブジェクト ファイルに生成します。

http://sources.redhat.com/gdb/onlinedocs/stabs.html

オペレーティング システムには、デバッガーがプロセスにアタッチしてプロセスの実行を制御できるようにする機能があります。一時停止、シングル ステップなど。

デバッガーがプログラムに接続されると、デバッガーは、デバッグ情報でプログラム アドレスの意味を調べることによって、プロセス スタックとプログラム カウンターをシンボリック形式に変換します。

動的言語は通常、インタープリターであろうとバイトコード VM であろうと、仮想マシンで実行されます。デバッガーがプログラム フローを制御し、プログラムの状態を検査できるようにするためのフックを提供するのは VM です。

于 2010-07-09T21:47:34.103 に答える