1

valgrind の一部である callgrind ツールの出力用の Boost.Spirit パーサーを作成しようとして行き詰まりました。Callgrind はドメイン固有の組み込みプログラミング言語 (DSEL) を出力します。これを使用すると、合成カウンターのカスタム式など、あらゆる種類の優れた機能を実行できますが、解析するのは簡単ではありません。

サンプルの callgrind 出力をhttps://gist.github.com/ned14/5452719#file-sample-callgrind-outputに配置しました。https://gist.github.com/ned14/5452719#file-callgrindparser-hppおよびhttps://gist.github.com/ned14/5452719で、Boost.Spirit lexer およびパーサーに現在の最善の試みを行いました。 #file-callgrindparser-cxx . レクサー部分は簡単です。タグ値、空白以外のテキスト、コメント、行末、整数、16 進数、浮動小数点数、および演算子をトークン化します (サンプル コードの句読点は無視してください。これらは使用されていません)。空白はスキップされます。

ここまでは順調ですね。問題は、トークン化された入力ストリームの解析です。私はまだ主要なスタンザを試みていませんが、ファイル内の任意の場所で発生する可能性のあるタグ値を解析しようとしています。タグの値は次のようになります。

tagtext: unknown series of tokens<eol>

自由形式のテキストでもかまいません。

desc: I1 cache: 32768 B, 64 B, 8-way associative, 157 picosec hit latency

この状況では、一連のトークンを文字列、つまり iterator_range に変換して抽出する必要があります。

ただし、式の場合もあります。

event: EPpsec = 316 Ir + 1120 I1mr + 1120 D1mr + 1120 D1mw + 1362 ILmr + 1362 DLmr + 1362 DLmw

これは、これから、イベント EPpsec は、I1mr に 1120 を掛けたものに Ir を掛けた 316 を足したものとして合成されることを意味します。

ここで言いたいのは、タグと値のペアを任意のトークンのセットとして蓄積し、後処理して後で変換する必要があるということです。

そのために、Boost.Spirit の utree() クラスはまさに私が求めていたものであり、それがサンプル コードで使用されています。しかし、VS2012 で 11 月の CTP コンパイラを可変個引数テンプレートとともに使用すると、現在、次のコンパイル エラーが発生しています。

1>C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(56): error C2440: 'static_cast' : cannot convert from 'boost::spirit::detail::list::node_iterator<const boost::spirit::utree>' to 'base_iterator_type'
1>          No constructor could take the source type, or constructor overload resolution was ambiguous
1>          C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(186) : see reference to function template instantiation 'IteratorT boost::iterator_range_detail::iterator_range_impl<IteratorT>::adl_begin<const Range>(ForwardRange &)' being compiled
1>          with
1>          [
1>              IteratorT=base_iterator_type
1>  ,            Range=boost::spirit::utree
1>  ,            ForwardRange=boost::spirit::utree
1>          ]

...これは、前方反復子の性質のためのistreambuf_iteratorのBoost.Spirit multi_pass<>ラップである私のbase_iterator_typeが、Boost.Spiritのutree()実装によって何らかの形で理解されていないことを示唆しています。つまり、これが私の悪いコードなのか、それとも悪い Boost.Spirit コードなのかはわかりません。

過去の Stackoverflow ヘルプのおかげで、トークン化されていない純粋な文法を書くことができましたが、それはもろいでしょう。適切な解決策は、トークン化して、かなり任意の入力が可能な自由形式の文法を使用することです。Boost.Spirit の Lex と Grammar を実際の例で連携させてこれを達成した例は、おもちゃの例ではなく、残念ながら非常に少ないです。したがって、どんな助けでも大歓迎です。

ニール

4

1 に答える 1

3

token_typetoken 属性はバリアントを公開します。これは、ベース イテレータの範囲に加えて、 typedefで宣言された型を _assume できます。

typedef lex::lexertl::token<base_iterator_type, mpl::vector<std::string, int, double>> token_type;

だから:stringintそしてdouble。ただし、可能な型の 1つへの強制は、パーサーが実際に値を使用するときに遅延してのみ発生することに注意してください。

utrees は非常に用途の広いコンテナーです[1]。したがって、spirit::utreeルールで属性を公開し、トークン値バリアントに iterator_range が含まれている場合、それをutreeオブジェクトに割り当てようとします (反復子が ... 「ファンキー」であるため、これは失敗します)。

目的の動作を取得する最も簡単な方法は、Qitagトークンの属性を文字列として解釈させ、それを. したがって、次の行は、コンパイルを成功させる修正を構成します。utree

    unknowntagvalue = qi::as_string[tok.tag] >> restofline;

ノート

これをすべて言ったので、私は実際に次のことを提案します

  • を使用して、一致したものに応じて異なる遅延ルールNabialek Trickをディスパッチすることを検討してください。これにより、後でraw を処理する必要がなくなります。tagutree

  • boost::spirit::traits::assign_to_XXXXXX特性の特化に成功した可能性があります(ドキュメントを参照)

  • 純粋な Qi パーサーの使用を検討してください。「もろくなる」というあなたの感情を「感じる」ことができますが[2]、あなたはすでに、それが正味のメリットを持たないほど複雑さを高めていることを実証しているようです:

    • 属性が具体化する予期しない方法 (この質問)
    • 行位置イテレータの問題(これはよくある質問であり、AFAIRにはほとんど難しいまたは洗練されていない解決策があります)
    • アドホック デバッグ (SA のソース データへのアクセス)、スキッパーの切り替え/無効化などに関する柔軟性の欠如。
    • 私の個人的な経験では、これらを駆動するためにレクサーの状態を調べることは役に立ちません。なぜなら、レクサーの状態の切り替えは からのみ確実に機能するためですlexer token semantic actions。一方、明確化はQi フェーズで行われることがよくあります。

しかし、私は発散しています:)


[1]たとえば、イテレータ範囲の非常に軽量な「参照」機能があります (たとえば、シンボルの場合、または必要でない限りソース バッファから属性に文字をコピーすることを避けるため)。

[2]事実上、シーケンシャル レクサー (スキャナー) を使用すると、バックトラックの機会が大幅に減少するため、パーサーのメンタル モデルが単純化されます。ただし、expectation pointsほぼ同じ効果を得るために使用できます。

于 2013-04-24T19:55:48.313 に答える