2

同じ属性を持つ 2 つのルールがあります。

matrix_ ルールの属性を matrixBlock_ 子ルールに渡すことは可能ですか? 繰り返しディレクティブがフォーム vector< > の属性を作成しないようにしたい。代わりに、matrix_ の属性 (numBlocks の時間) に書き込み続ける必要があります。属性を継承された属性として子ルールに渡そうとしたところ、コンパイルされました(以下を参照)。しかし、私のベクターには、phoenix::push_back からではない「ゴースト」エントリがいくつかあります。また、これは私にとって最適な方法ではないようです。セマンティック アクションの代わりに、matrixBlock_ で自動属性伝播を使用することは可能ですか?

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];
matrixBlock_ = *column[phoenix::push_back(_r1, _1)];

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, void(Matrix&), ascii::space_type> matrixBlock_;

アップデート

質問を明確にするために:

セマンティック アクションなしでルールを記述した場合、matrix_ の合成属性は次のようになります。

vector< vector< columnT > >

-

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_ ];
matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

1 次元配列の matrixBlock_ と同じ属性タイプを持たせたいと考えています。


私の実際の解決策は、ルールを 1 つだけ使用することです。(簡単に見えます :-) )

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];
//matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
//qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

アップデート

vs2010とboost 1.46.1でこのコードでファントムエントリを再現できました

http://liveworkspace.org/code/505091dc4631a379763567168a728e0c

出力: 42、45、-9、3、2、1、12、34、56、0、0、0

私の間違いは、古い Boost バージョンを使用していたことです。1.5 のファントムはありません。

これで、文法の 2 つの作業バージョンができました。push_back セマンティック アクションを使用せずに文法を再設計することは可能ですか?

4

1 に答える 1

4

更新しました

編集した質問への回答: はい、セマンティック アクションなしでこれを行うことができます。

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        matrixBlock_ = qi::lit(";") >> *qi::int_;
        matrix_      = qi::repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

行/列の数を検証したい場合があることに注意してください。それをチェックするために追加のセマンティック アクションを使用する私の拡張サンプルを参照してください (空の行を避けるために から*int_への微妙な変更に注意してください)。+int_

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx   = boost::phoenix;

typedef std::vector<int> Matrix;

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        using namespace qi;
        matrixBlock_ = lit(";") >> +int_ >> eps( 0 == (phx::size(_val) % 3));
        matrix_      = repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

int main()
{
    std::string test = ";42 45 -9; 3 2 1; 12 34 56";

    std::string::const_iterator f(test.begin()), l(test.end());

    Parser<std::string::const_iterator> parser;
    Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
        std::cout << "Wokay\n";
    else
        std::cerr << "Uhoh\n";

    std::cout << karma::format(karma::auto_ % ", ", m) << "\n";
}

古い答え:

はい、Spirit のカスタマイズ ポイントを使用して、ユーザー定義型をコンテナーとして扱うことができます。これについて私が提案するドキュメントエントリは次のとおりです。

ライブでの使用方法を示す簡単な例を次に示します。

一般に、「ファントム エントリ」に関する補足事項:

文法とコンテナ属性のバックトラッキングに関連する FAQ が少しあることに注意してください。問題は、パフォーマンス上の理由から、パーサーがバックトラック時に基になるコンテナーへの変更を元に戻さない (「ロールバック」) ことです。を使用してこの動作を強制することができますがqi::hold、文法を次のいずれかに再設計する価値がある場合があります。

  • バックトラックを避けるか、
  • 後の段階で属性にコミットする (セマンティック アクションを使用)

完全なコード サンプル:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix
{
   std::vector<int> data;
};

namespace boost { namespace spirit { namespace traits {
   template <>
      struct is_container<Matrix>
      {
      };

   template <typename Attrib>
      struct push_back_container<Matrix, Attrib>
      {
         static bool call(Matrix& c, Attrib const& val)
         {
            c.data.push_back(val);
            return true;
         }
      };

   template <>
      struct container_value<Matrix>
      {
         typedef int type;
      };
} } }

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
   Parser() : Parser::base_type(start)
   {
      start = *qi::int_;
   }
   qi::rule<It, Matrix(), qi::space_type> start;
};

int main()
{
   std::string test = "42 45 -9";

   std::string::const_iterator f(test.begin()),
      l(test.end());

   Parser<std::string::const_iterator> parser;
   Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
      std::cout << "Wokay\n";
   else
      std::cerr << "Uhoh\n";

   std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

出力:

Wokay
42, 45, -9

アップデート

もう少し背景:

もちろん、標準でサポートされているコンテナ タイプをラップするだけのこのような些細な例では、代わりにフュージョンアダプテーションを採用するのはかなり簡単です。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/struct.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix { std::vector<int> data; }; 
BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<int>, data));

int main()
{
    std::string test = "42 45 -9";
    std::string::const_iterator f(test.begin()), l(test.end());

    Matrix m;
    if (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))
        std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

qi::epsデータ要素を 1 つしか含まない構造体のバグ (AFAICT) のために、これが必要であることに注意してください。ここでの議論などを参照してください(および他のいくつかの言及)

于 2012-09-21T08:38:28.070 に答える