10

boost::spiritパーサーに別の問題があります。

template<typename Iterator>
struct expression: qi::grammar<Iterator, ast::expression(), ascii::space_type> {
    expression() :
        expression::base_type(expr) {
        number %= lexeme[double_];
        varname %= lexeme[alpha >> *(alnum | '_')];

        binop = (expr >> '+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1,_2)]
              | (expr >> '-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1,_2)]
              | (expr >> '*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1,_2)]
              | (expr >> '/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1,_2)] ;

        expr %= number | varname | binop;
    }

    qi::rule<Iterator, ast::expression(), ascii::space_type> expr;
    qi::rule<Iterator, ast::expression(), ascii::space_type> binop;
    qi::rule<Iterator, std::string(), ascii::space_type> varname;
    qi::rule<Iterator, double(), ascii::space_type> number;
};

これは私のパーサーでした。解析されて問題"3.1415"ありません"var"が、解析しようとする"1+2"と、それがわかりますparse failedbinop次に、ルールを次のように変更しようとしました

    binop = expr >>
           (('+' >> expr)[_val = construct<ast::binary_op<ast::add>>(_1, _2)]
          | ('-' >> expr)[_val = construct<ast::binary_op<ast::sub>>(_1, _2)]
          | ('*' >> expr)[_val = construct<ast::binary_op<ast::mul>>(_1, _2)]
          | ('/' >> expr)[_val = construct<ast::binary_op<ast::div>>(_1, _2)]);

しかし、現在はもちろんASTを構築することはできません。これは_1、と_2の設定が異なるためです。私は_r1言及されたようなものを見ただけですが、後押しとして-初心者として、私はどのようboost::phoenixにそしてboost::spirit相互作用するかを完全に理解することができません。

これを解決する方法は?

4

1 に答える 1

20

あなたが達成しようとしていることは、私には完全には明らかではありません。最も重要なのは、演算子の結合性について心配していませんか? 右再帰の使用に基づいた簡単な回答を示します。これにより、左結合演算子が解析されます。

あなたの目に見える質問に対する直接的な答えfusion::vector2<char, ast::expression>は、特にフェニックスラムダのセマンティックアクションでは、 a - をジャグリングすることです。(以下に、それがどのように見えるかを示します)。

その間、スピリットのドキュメントを読むべきだと思います

  • here in the old Spirit docs (左再帰を排除); 構文は適用されなくなりましたが、Spirit は引き続き LL 再帰降下パーサーを生成するため、左再帰の背後にある概念は引き続き適用されます。以下のコードは、これが Spirit Qi に適用されたことを示しています。
  • here : Qi の例には 3 つのcalculatorサンプルが含まれており、演算子の結合性が重要な理由と、2 項演算子の結合性を捉える文法をどのように表現するかについてのヒントが得られるはずです。明らかに、括弧で囲まれた式をサポートしてデフォルトの評価順序をオーバーライドする方法も示しています。

コード:

次のような入力を解析する、動作するコードの 3 つのバージョンがあります。

std::string input("1/2+3-4*5");

グループ化されたast::expressionものに (BOOST_SPIRIT_DEBUG を使用):

<expr>
  ....
  <success></success>
  <attributes>[[1, [2, [3, [4, 5]]]]]</attributes>
</expr>

コードへのリンクは次のとおりです。

ステップ 1:セマンティック アクションを減らす

まず、演算子ごとの代替解析式を取り除きます。これにより、過度のバックトラッキングが発生します1。また、ご存知のように、文法を維持するのが難しくなります。したがって、セマンティック アクションに関数を使用する、より単純なバリエーションを次に示します。

1 BOOST_SPIRIT_DEBUG を使用して確認してください!

static ast::expression make_binop(char discriminant, 
     const ast::expression& left, const ast::expression& right)
{
    switch(discriminant)
    {
        case '+': return ast::binary_op<ast::add>(left, right);
        case '-': return ast::binary_op<ast::sub>(left, right);
        case '/': return ast::binary_op<ast::div>(left, right);
        case '*': return ast::binary_op<ast::mul>(left, right);
    }
    throw std::runtime_error("unreachable in make_binop");
}

// rules:
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
binop = (simple >> char_("-+*/") >> expr) 
    [ _val = phx::bind(make_binop, qi::_2, qi::_1, qi::_3) ]; 

expr = binop | simple;

ステップ 2:冗長なルールを削除し、使用します_val

ご覧のとおり、これにより複雑さが軽減される可能性があります。binop 中間 (非常に冗長になっています) を削除するのは、今では小さなステップにすぎません。

number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
expr = simple [ _val = _1 ] 
    > *(char_("-+*/") > expr) 
            [ _val = phx::bind(make_binop, qi::_1, _val, qi::_2) ]
    > eoi;

ご覧のように、

  • exprルール内で、_valレイジー プレースホルダーは、binops を蓄積する疑似ローカル変数として使用されます。ルール全体でqi::locals<ast::expression>、そのようなアプローチに使用する必要があります。(これは に関するあなたの質問でした_r1)。
  • 明示的な期待値があり、文法がより堅牢になります
  • exprルールが自動ルールである必要がなくなった (のexpr =代わりにexpr %=)

ステップ 0:フュージョン タイプと直接格闘する

最後に、面白おかしく、_1、_2 などのシフト バインディングと共に、提案されたコードをどのように処理できるかを示しましょう。

static ast::expression make_binop(
        const ast::expression& left, 
        const boost::fusion::vector2<char, ast::expression>& op_right)
{
    switch(boost::fusion::get<0>(op_right))
    {
        case '+': return ast::binary_op<ast::add>(left, boost::fusion::get<1>(op_right));
        case '-': return ast::binary_op<ast::sub>(left, boost::fusion::get<1>(op_right));
        case '/': return ast::binary_op<ast::div>(left, boost::fusion::get<1>(op_right));
        case '*': return ast::binary_op<ast::mul>(left, boost::fusion::get<1>(op_right));
    }
    throw std::runtime_error("unreachable in make_op");
}

// rules:
expression::base_type(expr) {
number %= lexeme[double_];
varname %= lexeme[alpha >> *(alnum | '_')];

simple = varname | number;
binop %= (simple >> (char_("-+*/") > expr)) 
    [ _val = phx::bind(make_binop, qi::_1, qi::_2) ]; // note _2!!!

expr %= binop | simple;

make_binopご覧のとおり、そのように関数を書くのはそれほど楽しいものではありません!

于 2011-12-12T00:57:15.790 に答える