Racket マクロ システムを学習する演習として、 C++ の catch フレームワークに基づいて単体テスト フレームワークを実装してきました。そのフレームワークの機能の 1 つは、次のようなチェックを作成する場合です。
CHECK(x == y); // (check x y)
CHECK_EQUALS や CHECK_GREATER などのマクロを使用する必要がある他のテスト フレームワークとは異なり、使用されているマクロが完全に汎用的であっても、チェックに違反すると、エラー メッセージに x と y の値が出力されます。これはハッカーによって可能です。式テンプレートと演算子のオーバーロードが含まれます。
Racket では、さらに優れた仕事ができるはずだと思います。C++ バージョンでは、マクロは部分式の中を見ることができないので、次のように書くと:
CHECK(f(x, g(y)) == z); // (check (= (f x (g y)) z))
チェックに違反すると、x、y、または g(y) の値ではなく、等号の左側と右側の値のみが検出されます。ラケットでは、部分式に再帰して、評価の各ステップを示すツリーを出力できるはずです。
問題は、これを行うための最良の方法が何であるかわからないことです:
- 私は構文解析にかなり慣れてきましたが、これはその能力を超えているようです。
- #%app のカスタマイズについて読みましたが、これはほとんど私が望んでいるように見えますが、たとえば f がマクロの場合、展開にある式のすべての評価を出力したくはありません。ユーザーがチェック マクロを呼び出したときに表示されていました。また、言語を定義せずに使用できるかどうかもわかりません。
- syntax-parameterize を使用して基本的な演算子の意味を乗っ取ることができますが、g(y) のような関数呼び出しには役立ちません。
- syntax->datum を使用して AST を手動でウォークし、部分式で eval を自分で呼び出すことができました。これは難しいようです。
- トレース ライブラリは、私が望むことをほとんど行っているように見えますが、事前に関数のリストを提供する必要があり、出力先を制御するようには見えません (成功したかどうかではなく、チェックが失敗したため、実行が進むにつれて中間値をサイドに保存する必要があります)。
これを実装するための最良の、または少なくとも慣用的な方法は何でしょうか?