9

次のようなツリー式のような C 関数を解析しようとしています ( Spirit Parser Frameworkを使用):

F( A() , B( GREAT( SOME , NOT ) ) , C( YES ) )

このために、次の文法で 3 つの規則を使用しようとしています。

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> {

    InputGrammar() : InputGrammar::base_type( ) {
       tag = ( qi::char_("a-zA-Z_")  >> *qi::char_("a-zA-Z_0-9") )[ push_back( at_c<0>(qi::_val) , qi::_1 ) ];
       command =  tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",")
                                        [ push_back( at_c<1>(qi::_val) , qi::_1 ) ]  >> ")";
       instruction = ( command | tag ) [qi::_val = qi::_1];
    }
    qi::rule< Iterator , ExpressionAST() , space_type > tag;
    qi::rule< Iterator , ExpressionAST() , space_type > command;
    qi::rule< Iterator , ExpressionAST() , space_type > instruction;
};

このタグ ルールは、式で使用される識別子 (「関数」名) を取得しようとするだけであることに注意してください。また、ほとんどの例のように、タグ ルールの署名がExpressionASTではなく を返すことにも注意してください。std::stringこのようにしたい理由は、実際には非常に単純です。バリアントを使用するのが嫌いで、可能であれば使用しないようにします。ケーキを持っておいて、それを食べるのもいいと思います。

コマンドは、タグ (現在のノードの名前、AST ノードの最初の文字列フィールド) と括弧で囲まれた可変数の引数で開始する必要があり、各引数はタグ自体または別のコマンドにすることができます。

ただし、この例はまったく機能しません。それはすべてコンパイルされますが、実行時にすべてのテスト文字列の解析に失敗します。そして、私を本当に悩ませているのは、少なくとも単語の伝統的な意味では、上記のコードを実際にデバッグできないため、修正方法がわからないことです。基本的に、上記のコードを修正できる唯一の方法は、自分が間違っていることを知ることです。

したがって、問題は、上記のコードの何が問題なのかわからないということです。上記の文法をどのように定義しますか?

ExpressionAST私が使用しているタイプは次のとおりです。

struct MockExpressionNode {
    std::string name;
    std::vector< MockExpressionNode > operands;

    typedef std::vector< MockExpressionNode >::iterator iterator;
    typedef std::vector< MockExpressionNode >::const_iterator const_iterator;

    iterator begin() { return operands.begin(); }
    const_iterator begin() const { return operands.begin(); }
    iterator end() { return operands.end(); }
    const_iterator end() const { return operands.end(); }

    bool is_leaf() const {
        return ( operands.begin() == operands.end() );
    }
};

BOOST_FUSION_ADAPT_STRUCT(
    MockExpressionNode,
    (std::string, name)
    (std::vector<MockExpressionNode>, operands)
)
4

1 に答える 1

12

デバッグに関しては、通常のブレーク アンド ウォッチ アプローチを使用できます。ただし、ルールをどのようにフォーマットしたかによって、これは困難になります。スピリットの例 (1 行に 1 つのパーサー、1 行に 1 つのフェニックス ステートメント) に従って書式を設定すると、ブレーク ポイントはより多くの情報を提供します。

A()あなたのデータ構造には、両方が葉であるという点で区別する方法がありませんSOME(何か不足している場合はお知らせください)。あなたのバリアント コメントから、これはあなたの意図ではないと思うので、これら 2 つのケースを区別するために、bool commandFlagMockExpressionNode にメンバー変数 ( の場合は true 、 の場合はA()false SOME) を、対応する Fusion アダプター行と共に追加しました。

具体的なコードについては、開始規則を基本コンストラクターに渡す必要があります。つまり、次のようになります。

InputGrammar() : InputGrammar::base_type(instruction) {...}

これが文法のエントリ ポイントであり、データが解析されなかった理由です。それなしでコンパイルされたことに驚いています。最初のルールのタイプと一致するために文法タイプが必要だと思っていました。それでも、これは従うのに便利な規則です。

tagルールの場合、実際には 2 つのパーサーがあり、 1 つは typeのqi::char_("a-zA-Z_")_1 で、もう1 つは type (基本的に) の _2です。自動ルールなしでこれらを強制的に文字列にすることはできませんが、解析された各文字にルールを追加することで実行できます。char*qi::char_("a-zA-Z_0-9")vector<char>

tag =   qi::char_("a-zA-Z_")
        [ at_c<0>(qi::_val) = qi::_1 ];
    >> *qi::char_("a-zA-Z_0-9")           //[] has precedence over *, so _1 is 
        [ at_c<0>(qi::_val) += qi::_1 ];  //  a char rather than a vector<char>

ただし、スピリットにこの変換を行わせる方がはるかにクリーンです。したがって、新しいルールを定義します。

qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");

そして、それについて心配しないでください;)。その後、タグは

tag = identifier
      [
          at_c<0>(qi::_val) = qi::_1,
          ph::at_c<2>(qi::_val) = false //commandFlag
      ]

コマンドについては、最初の部分は問題ありませんが、いくつか問題があり(*instruction >> ",")[ push_back( at_c<1>(qi::_val) , qi::_1 ) ]ます。これは、「,」が後に続くゼロまたは複数の命令ルールを解析します。また、a を push_back しようとしますvector<MockExpressionNode>(これがコンパイルされた理由も不明です。開始ルールがないためにインスタンス化されていない可能性があります)。次のものが必要だと思います(識別子を変更して):

command =
        identifier
        [
           ph::at_c<0>(qi::_val) = qi::_1, 
           ph::at_c<2>(qi::_val) = true    //commandFlag
        ]
    >>  "("
    >> -(instruction % ",")
        [
           ph::at_c<1>(qi::_val) = qi::_1
        ]
    >>  ")";

これは、オプションの operator-と list operator を使用します。%後者は と同等instruction >> *("," >> instruction)です。次に、phoenix 式はベクターを構造体メンバーに直接割り当てるだけですが、アクションを命令の一致に直接割り当てて、push_back を使用することもできます。

命令規則は問題ありませんinstruction %= (command|tag)

A()最後に、実際に と の間に区別がない場合SOME(つまり、元の構造に no がないcommandFlag場合)、autorules のみを使用してこのパーサーを作成できます。

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> {
   InputGrammar() : InputGrammar::base_type( command ) {
      identifier %=
             qi::char_("a-zA-Z_")
         >> *qi::char_("a-zA-Z_0-9");
      command %=
            identifier
         >> -(
            "("
         >> -(command % ",")
         >>  ")");
    }
    qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
    qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command;
};

これは、入力を厳密にモデル化するフュージョン ラップ構造を使用することの大きな利点です。

于 2010-06-20T04:20:41.697 に答える