I'm trying to set up a parser which, given a value, can assign it to a certain element of a vector, but I'm not entirely sure how to implement it.
Let's say the following piece of code parses the string (0){**+*+}
. It should increment bar.a[0]
once for every +
, and bar.b[0]
once for every *
. The issue I'm encountering is that I'm not sure how to get a reference to a vector's element using _a
:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <vector>
//The struct containing the vector.
struct testStruct {
std::vector<int> a, b;
};
BOOST_FUSION_ADAPT_STRUCT (
testStruct,
(std::vector<int>, a)
(std::vector<int>, b)
)
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
namespace ascii = boost::spirit::ascii;
template<typename Iterator>
struct foo : qi::grammar<Iterator, testStruct(), qi::locals<unsigned>, ascii::space_type> {
foo() : foo::base_type(start) {
using namespace qi::labels;
using qi::eps;
using qi::lit;
using qi::uint_;
using phoenix::at_c;
start = lit('(')
>> uint_ [_a = _1]
>> ')'
>> '{'
>> starsOrPlus(
at_c<_a>(at_c<0>(_val)), //This is where I'm not sure what to do.
at_c<_a>(at_c<1>(_val))
)
>> '}'
;
starsOrPlus = eps [_r1 = 0]
[_r2 = 0]
>> (
* (
(
+lit('+') [_r1 += 1]
)
^ (
+lit('*') [_r2 += 1]
)
)
)
;
}
qi::rule<Iterator, testStruct(), qi::locals<unsigned>, ascii::space_type> start;
//Parses stars and pluses. Sets the first uint8_t to the number of *, and the
//second to the number of +.
qi::rule<Iterator, void(int&, int&), ascii::space_type> starsOrPlus;
};
//Main program
int main() {
std::string testString = "(2){**++*+}";
typedef foo<std::string::const_iterator> foo;
foo grammar;
testStruct bar;
std::string::const_iterator iter = testString.begin();
std::string::const_iterator end = testString.end();
bool parsed = phrase_parse(iter, end, grammar, ascii::space, bar);
if (parsed) {
//Do something with the data...
}
return 0;
}
This fails to compile with the following errors:
main.cpp||In constructor 'foo<Iterator>::foo()':|
main.cpp|36|error: 'boost::spirit::_a' cannot appear in a constant-expression|
main.cpp|36|error: no matching function for call to 'at_c(boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::at_eval<0>, boost::fusion::vector<boost::spirit::attribute<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&)'|
main.cpp|37|error: 'boost::spirit::_a' cannot appear in a constant-expression|
main.cpp|37|error: no matching function for call to 'at_c(boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::at_eval<1>, boost::fusion::vector<boost::spirit::attribute<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > >&)'|
So, clearly, I can't use a placeholder value within at_c
. I'm also aware that, even if I could, there would also be the issue of re-sizing the vector if the given position is out of range.
How would I implement something like this? Am I just going about this entirely the wrong way?
This should get you started:
#include <vector>
#include <string>
#include <iostream>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
struct testStruct
{
std::vector<int> a, b;
};
BOOST_FUSION_ADAPT_STRUCT
(
testStruct,
(std::vector<int>, a)
(std::vector<int>, b)
)
namespace bp = boost::phoenix;
namespace bs = boost::spirit;
namespace bsq = bs::qi;
template<typename Iterator>
struct foo : bsq::grammar<Iterator, testStruct(), bsq::locals<unsigned> >
{
foo() : foo::base_type(start)
{
using namespace bs::labels;
using bp::at_c;
using bp::resize;
using bs::lit;
using bs::uint_;
start
= '('
>> uint_
[
_a = _1,
resize(at_c<0>(_val), _1 + 1),
resize(at_c<1>(_val), _1 + 1)
]
>> "){"
>> starsOrPlus(at_c<0>(_val)[_a], at_c<1>(_val)[_a])
>> '}'
;
starsOrPlus
= *(
lit('+')[_r1 += 1]
| lit('*')[_r2 += 1]
)
;
}
bsq::rule<Iterator, testStruct(), bsq::locals<unsigned> > start;
bsq::rule<Iterator, void(int&, int&)> starsOrPlus;
};
void printvec(std::vector<int> const& vec)
{
bool first = true;
for (std::vector<int>::const_iterator it = vec.begin(), it_end = vec.end();
it != it_end;
++it)
{
if (first)
first = false;
else
std::cout << ", ";
std::cout << *it;
}
}
int main()
{
foo<std::string::const_iterator> grammar;
testStruct bar;
std::string const input = "(2){**++*}";
std::string::const_iterator first = input.begin(), last = input.end();
if (bsq::parse(first, last, grammar, bar) && first == last)
{
std::cout << "bar.a: ";
printvec(bar.a);
std::cout << "\nbar.b: ";
printvec(bar.b);
std::cout << '\n';
}
else
std::cout << "parse failed\n";
}
The notable changes here are:
vector
s inside of testStruct
must be resized to a size sufficient for the desired index to be validoperator[]
is used instead of boost::phoenix::at_c
to access the index of a vector
, just as one would in "normal" codeNote that I took out the skip parser to simplify things (and because it didn't appear to be necessary); add it back if you need to -- it had no real relevance here.