Updated
Answering your edited question: Yes you can do this without semantic actions, doing simply:
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_;
};
Note that you may want to validate the number of rows/columns. See my extended sample, which uses an extra semantic action to check that (note the subtle change from *int_
to +int_
to avoid empty rows):
#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
";
else
std::cerr << "Uhoh
";
std::cout << karma::format(karma::auto_ % ", ", m) << "
";
}
Old answer:
Yes, you can use Spirit's customization points to treat your user-defined type as a container. The documentation entry I'd suggest for this is here:
Here is a simple example showing how to use it, live:
Side note with regards to 'phantom entries', in general:
Note that there is a bit of a FAQ related to backtracking grammars and container attributes. The thing is, for performance reasons, parsers won't undo ('rollback') changes to their underlying containers on backtracking. You can force this behaviour using qi::hold
but it may worth the effort to redesign the grammar to either
- avoid backtracking or
- commit to the attribute at a later stage (using semantic actions)
Full code sample:
#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
";
else
std::cerr << "Uhoh
";
std::cout << karma::format(karma::auto_ % ", ", m.data) << "
";
}
Output:
Wokay
42, 45, -9
Update
A little more background:
Of course, for a trivial example like this, that just wraps a standard supported container type, it would be fairly easy to employ fusion adaptation instead: ( http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd )
#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) << "
";
}
Note that the qi::eps
is necessary due to a bug (AFAICT) with structs that contain only one data element. See e.g. discussion here (and some other mentions)