更新しました
編集した質問への回答: はい、セマンティック アクションなしでこれを行うことができます。
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) のために、これが必要であることに注意してください。ここでの議論などを参照してください(および他のいくつかの言及)