web-dev-qa-db-ja.com

インタープリター言語パーサーのテスト戦略

最近の個人的なプロジェクトのために、私は自分のプログラミング言語の通訳に取り組み始めました。このプロジェクトで自分で設定した基本ルールの1つは、開発するコードをできるだけ多く適切にテストする必要があるということです。

パーサーのテストに関するガイドやディスカッションを探してWebを検索しましたが、このトピックを真正面から扱っているリソースはほとんどありません。実際、私が見つけたいくつかのリソースは、本質的に、解析ケースをテストするための一種の「強引な」アプローチを示唆しており、最終的には次のように要約されます。

  • パーサーから抽象構文ツリーを作成し、既知の良好なASTに対して検証します。
  • パーサーから抽象構文ツリーを作成し、S式のようなものに「シリアル化」して、既知の適切な値に対してS式を検証します。
  • 抽象構文ツリーから仮想マシンのバイトコードを生成し、既知の適切な値と照合します。

私がリストした3つのオプションのうち、3番目のオプションが最も好まれます。これは、テストコードを明示的なAST構造から切り離し、テストコードのバグが発生しにくいように見える(オペコードの配列の同等性をテストする)ためです。値は、ネストされたAST要素)の同等性を検証するよりもエラーが発生しにくいようです。

それでも、私はCでインタープリターを作成しており、バイトコードにリテラルを指定するのは面倒で冗長です。テストと期待される結果を指定するための、より簡潔でありながら同様に徹底的な方法を見つけることができるかもしれないと期待していました。

多くのテストフレームワークには、テストでのランダム化のサポートが組み込まれていることを知っています。数百または数千の決定論的な例を手作業で書くのではなく、何らかの形でいくつかの式の断片を指定して、それらをパーサーにランダムに送信できればと思います。ただし、この戦略では、テストスイートでもパーサーロジックを本質的に書き直すことなく、コンパイラ出力の正確さをどのように検証できるかは不明です。

TL; DR?

  1. パーサーの出力を検証するための明らかなテスト戦略が欠けていますか?幅広いテストをより簡潔に書くことはできますか、それとも数百または数千のテストを手作業でコーディングする必要がありますか?
  2. この問題にランダム化を採用できますか?もしそうなら、どのように?

注意

パーサジェネレータのようなものを使用して、機能するパーサを生成できることはわかっています。私はこのプロジェクトを開始して、さまざまな部分がどのように機能し、連携するかを学びました。そこで、すべてを手書きで書くことにしました。

4
Chris

私はおそらく少なくとも2種類のテストを書くでしょう。既知のテキストを使用してテストを作成し、それを「ASTまたはエラー」と比較します。また、提供されたASTを取得し、実行を検証するテストを作成します(バイトコードエンジンを個別にテストして、「正しいバイトコードが生成されることを検証する」、または単に「既知の状態で実行し、結果の状態を検証する」)。

ASTをテストする方法はそれほど重要ではありません。シリアル化が決定論的である限り、構造の比較またはシリアル化のどちらでも問題なく機能するはずです。

2
Vatine

実用的なアプローチは、多くのテスト例との統合テストを書くことです。

対応する出力(結果またはエラーメッセージ)を含む入力用の例の大きなリポジトリを作成します。

自動テストは例を繰り返し処理し、インタープリターの出力が期待される出力と一致することを確認します。

 example: valid math calculation
 source: "Sqrt(49)"
 expected errormessage: none
 expected output: "7"

 example: error in math calculation
 source: "sqrt(-1)" 
 expected errormessage: "Illegal Argument exception: Cannot calculate squareroot from negative number"
 expected Java output: none

重複ではありませんが、密接に関連しています: BDDを使用してコンパイラーの単体テストを行う方法は?

1
k3b