いくつかの質問で、 boost.orgからのSpiritパーサー ジェネレーター フレームワークの推奨事項を見てきましたが、コメントには、満足していない Spirit を使用している人々からの不平があります。それらの人々は立ち上がって、スピリットを使用することの欠点や欠点を私たちに説明してくれませんか?
5 に答える
とてもクールなアイデアで、気に入りました。C++ テンプレートの使い方を実際に学べたことは特に役に立ちました。
しかし、彼らのドキュメントでは、小規模から中規模のパーサーには Spirit を使用することを推奨しています。完全な言語のパーサーは、コンパイルに何年もかかります。その理由を3つ挙げます。
スキャナレス解析。非常に単純ですが、バックトラックが必要な場合、パーサーの速度が低下する可能性があります。ただし、これはオプションです。レクサーが統合されている場合があります。Spirit で構築された C プリプロセッサを参照してください。約 300 行の文法 (.h ファイルと .cpp ファイルの両方を含む) は、GCC で 6M のファイルにコンパイル (最適化されていない) します。インライン化と最大の最適化により、最大 1,7M まで削減されます。
解析が遅い - 文法の静的チェックはなく、過剰な先読みが必要であることを示唆したり、たとえば左再帰の使用などの基本的なエラーを検証したりしません (再帰降下パーサー LL 文法では無限再帰につながります)。ただし、左再帰は追跡するのが非常に難しいバグではありませんが、過剰な先読みは指数関数的な解析時間を引き起こす可能性があります。
テンプレートの使用量が多い - これには一定の利点がありますが、コンパイル時間とコード サイズに影響します。さらに、文法定義は通常、他のすべてのユーザーに表示される必要があるため、コンパイル時間はさらに長くなります。適切なパラメーターを使用して明示的なテンプレートのインスタンス化を追加することで、文法を .cpp ファイルに移動できましたが、簡単ではありませんでした。
更新: 私の回答は、Spirit V2 ではなく、Spirit クラシックでの私の経験に限定されています。私は依然として、Spirit がかなりテンプレートに基づいていることを期待していますが、今は推測にすぎません。
boost 1.41 では、Spirit の新しいバージョンがリリースされており、spirit::classic を大幅に凌駕しています。
長い間ベータ版 (Spirit 2.0 で 2 年以上) を経て、まもなくリリースされる Boost 1.41 で、Spirit 2.1 がついにリリースされます。コードは現在非常に安定しており、本番コードの準備が整っています。Boost 1.41 に間に合うようにドキュメントを完成させるために懸命に取り組んでいます。ここでドキュメントの現在の状態を確認できます。現在、Boost SVN トランクでコードとドキュメントを見つけることができます。Spirit を含む新しいプロジェクトがある場合は、Spirit 2.1 から始めることを強くお勧めします。Spirit メーリングリストからの OvermindDL の投稿を引用させてください。
これを頻繁に言うとボットのように聞こえるかもしれませんが、Spirit.Classic は古くからあるため、Spirit2.1 に切り替える必要があります。これにより、はるかに少ないコードで、はるかに簡単に実行できるすべての操作を実行できます。もっと早く。たとえば、Spirit2.1 では、AST 全体をインラインで構築できます。変なオーバーライドや、後で構築する必要はありません。すべて 1 つの優れた高速ステップです。本当に更新する必要があります。Spirit2.1 のドキュメントなどへのリンクについては、過去の他の投稿を参照してください。Spirit2.1 は現在 Boost Trunk にありますが、Boost 1.41 で正式にリリースされますが、それ以外は完成しています。
私にとって最大の問題は、コンパイラやデバッガから見た Spirit の式がかなり長いことです ( Spirit Classic の 1 つの式の一部を下にコピーしました)。これらの表現は私を怖がらせます。Spirit を使用するプログラムに取り組んでいるとき、valgrind を使用したり、gdb にバックトレースを出力したりするのが怖いです。
boost::spirit::classic::parser_result<boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<optional_suffix_parser<char const*>, boost::spirit::classic::ref_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::ref_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > >, boost::spirit::classic::kleene_star<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > > > > > >, void ()(char const, char const*)>, boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> > >::type boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<
私が気に入らない点は次のとおりです。
ドキュメントは限られています。「すべて」が説明されている 1 つの大きな Web ページがありますが、現在の説明では詳細が不足しています。
貧弱な AST 生成。AST はあまり説明されておらず、AST 修飾子がどのように機能するかを理解するために頭を壁にぶつけた後でも、簡単に操作できる AST (つまり、問題のドメインに適切に対応するもの) を取得することは困難です。
「中」サイズの文法であっても、コンパイル時間が大幅に増加します
構文が重すぎます。C/C++ では、コードを複製しなければならない (つまり、宣言と定義の間) というのは現実です。ただし、boost::spirit では、grammar<> を宣言するときに、いくつかのことを 3 回繰り返す必要があるようです:D (AST が必要な場合は、これが必要です:D)
これ以外は、C++ の制限を考慮して、パーサーでかなり良い仕事をしたと思います。しかし、私は彼らがそれをもっと改善すべきだと思います。歴史のページには、現在の「静的な」精神の前に「動的な」精神があったことが記載されています。どれだけ高速で、構文がどれだけ優れているか疑問に思っています。
最大の問題は、文法の問題に対する診断やその他のヘルプがないことだと思います。文法があいまいな場合、パーサーは期待どおりに解析しない可能性があり、それに気付く良い方法はありません。