[Boost.Spirit] Synthesised attributes and sub-rules

Hi, Please have a look at the code below. I would like to be able to split up the start rule into several sub-rules (currently commented out). The problem is, I'm not sure how to reference the synthesised attribute from the grammar in these sub-rules? Is it possible? I would like to do this so that the grammar is more readable, as the actual rules have a bit more complexity than in this simple example. Thank you, Vitaly ---------------- #include <boost/spirit/home/phoenix/bind/bind_function.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/tuple/tuple.hpp> #include <algorithm> #include <iterator> #include <string> #include <vector> using namespace boost::spirit; using namespace boost::spirit::qi; struct a_: qi::symbols<char, int> { a_() { add ("A", 0) ("B", 1) ("C", 2) ; } } a; struct b_: qi::symbols<char, boost::tuple<int, int> > { b_() { add ("x", boost::make_tuple(3, 2)) ("y", boost::make_tuple(4, 0)) ("z", boost::make_tuple(5, 1)) ; } } b; struct c_: qi::symbols<char, int> { c_() { add ("E", 6) ("D", 7) ("J", 8) ; } } c; void insert_a(const int &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; } for(unsigned int i = 0; i < c; ++i) { result.push_back(value); } } void insert_b(const boost::tuple<int, int> &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; } for(unsigned int i = 0; i < c; ++i) { result.push_back(value.get<0>()); result.push_back(value.get<1>()); } } void insert_c(const int &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; } for(unsigned int i = 0; i < c; ++i) { result.push_back(value); } } template<typename Iterator> struct test_grammar: qi::grammar<Iterator, std::vector<int>()> { test_grammar(): test_grammar::base_type(start) { using boost::phoenix::bind; /*a_rule = ( +(a >> -uint_)[bind(insert_a, _1, _2, _val)] ) ; b_rule = ( +(b >> -uint_)[bind(insert_b, _1, _2, _val)] ) ; c_rule = ( +(c >> -uint_)[bind(insert_c, _1, _2, _val)] ) ;*/ start = ( +(a >> -uint_)[bind(insert_a, _1, _2, _val)] | +(b >> -uint_)[bind(insert_b, _1, _2, _val)] | +(c >> -uint_)[bind(insert_c, _1, _2, _val)] ) ; } qi::rule<Iterator> a_rule; qi::rule<Iterator> b_rule; qi::rule<Iterator> c_rule; qi::rule<Iterator, std::vector<int>()> start; }; int main() { std::string s("A3BAC6"); typedef std::string::iterator string_iterator; string_iterator first = s.begin(); string_iterator last = s.end(); typedef test_grammar<string_iterator> test_grammar; test_grammar test_parser; std::vector<int> v; bool result = parse(first, last, test_parser, v); if(result && first == last) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); } std::string s2("xy2z3"); string_iterator first2 = s2.begin(); string_iterator last2 = s2.end(); bool result2 = parse(first2, last2, test_parser, v); if(result2 && first2 == last2) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); } std::string s3("EDJ"); string_iterator first3 = s3.begin(); string_iterator last3 = s3.end(); bool result3 = parse(first3, last3, test_parser, v); if(result3 && first3 == last3) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); } return 0; }

Please have a look at the code below. I would like to be able to split up the start rule into several sub-rules (currently commented out). The problem is, I'm not sure how to reference the synthesised attribute from the grammar in these sub-rules? Is it possible? I would like to do this so that the grammar is more readable, as the actual rules have a bit more complexity than in this simple example.
That should be easy, see below.
#include <boost/spirit/home/phoenix/bind/bind_function.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/tuple/tuple.hpp> #include <algorithm> #include <iterator> #include <string> #include <vector>
using namespace boost::spirit; using namespace boost::spirit::qi;
struct a_: qi::symbols<char, int> { a_() { add ("A", 0) ("B", 1) ("C", 2) ; }
} a;
struct b_: qi::symbols<char, boost::tuple<int, int> > { b_() { add ("x", boost::make_tuple(3, 2)) ("y", boost::make_tuple(4, 0)) ("z", boost::make_tuple(5, 1)) ; }
} b;
struct c_: qi::symbols<char, int> { c_() { add ("E", 6) ("D", 7) ("J", 8) ; }
} c;
void insert_a(const int &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; }
for(unsigned int i = 0; i < c; ++i) { result.push_back(value); } } void insert_b(const boost::tuple<int, int> &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; }
for(unsigned int i = 0; i < c; ++i) { result.push_back(value.get<0>()); result.push_back(value.get<1>()); } } void insert_c(const int &value, const boost::optional<unsigned int> &count, std::vector<int> &result) { unsigned int c = 1; if(count) { c += *count; }
for(unsigned int i = 0; i < c; ++i) { result.push_back(value); } }
template<typename Iterator> struct test_grammar: qi::grammar<Iterator, std::vector<int>()> { test_grammar(): test_grammar::base_type(start) { using boost::phoenix::bind;
/*a_rule = ( +(a >> -uint_)[bind(insert_a, _1, _2, _val)] ) ;
b_rule = ( +(b >> -uint_)[bind(insert_b, _1, _2, _val)] ) ;
c_rule = ( +(c >> -uint_)[bind(insert_c, _1, _2, _val)] ) ;*/
start = ( +(a >> -uint_)[bind(insert_a, _1, _2, _val)] | +(b >> -uint_)[bind(insert_b, _1, _2, _val)] | +(c >> -uint_)[bind(insert_c, _1, _2, _val)] ) ;
a_rule = +(a >> -uint_)[bind(insert_a, _1, _2, _val)]; b_rule = +(b >> -uint_)[bind(insert_b, _1, _2, _val)]; c_rule = +(c >> -uint_)[bind(insert_c, _1, _2, _val)]; start = a_rule | b_rule | c_rule;
}
qi::rule<Iterator> a_rule; qi::rule<Iterator> b_rule; qi::rule<Iterator> c_rule;
qi::rule<Iterator, std::vector<int>()> a_rule; qi::rule<Iterator, std::vector<int>()> b_rule; qi::rule<Iterator, std::vector<int>()> c_rule;
qi::rule<Iterator, std::vector<int>()> start; };
int main() { std::string s("A3BAC6");
typedef std::string::iterator string_iterator; string_iterator first = s.begin(); string_iterator last = s.end(); typedef test_grammar<string_iterator> test_grammar; test_grammar test_parser;
std::vector<int> v; bool result = parse(first, last, test_parser, v); if(result && first == last) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); }
std::string s2("xy2z3"); string_iterator first2 = s2.begin(); string_iterator last2 = s2.end(); bool result2 = parse(first2, last2, test_parser, v); if(result2 && first2 == last2) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); }
std::string s3("EDJ"); string_iterator first3 = s3.begin(); string_iterator last3 = s3.end(); bool result3 = parse(first3, last3, test_parser, v); if(result3 && first3 == last3) { std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; } else { std::cerr << "Failed to parse expression\n"; v.clear(); }
return 0; }
HTH Regards Hartmut --------------- http://boost-spirit.com

On 14 October 2010 22:36, Hartmut Kaiser <hartmut.kaiser@gmail.com> wrote:
That should be easy, see below.
start = a_rule | b_rule | c_rule;
qi::rule<Iterator, std::vector<int>()> a_rule; qi::rule<Iterator, std::vector<int>()> b_rule; qi::rule<Iterator, std::vector<int>()> c_rule;
HTH Regards Hartmut --------------- http://boost-spirit.com
I think I tried something like that, but it didn't work. Unless I'm mistaken, each of those rules will create their own std::vector<int> instance, so the values will not end up inserted into the vector specified in the grammar parameter. I was able to eventually achieve my goal by specifying each of the rules like so: qi::rule<Iterator, std::vector<int> &> a_rule; and in the main grammar I passed the _val into them. start = a_rule(_val) | b_rule(_val) | c_rule(_val) Then I referenced the arguments with _r1 in the sub-rules. Does that look correct? Is there a better way?

On 14 October 2010 22:36, Hartmut Kaiser <hartmut.kaiser@gmail.com> wrote:
That should be easy, see below.
start = a_rule | b_rule | c_rule;
<snip>
qi::rule<Iterator, std::vector<int>()> a_rule; qi::rule<Iterator, std::vector<int>()> b_rule; qi::rule<Iterator, std::vector<int>()> c_rule;
qi::rule<Iterator, std::vector<int>()> start;
Hi Hartmut, Thanks for your help. I've upgraded my version of Spirit to the one from Boost 1.44, so things work better now. However, I'm still unable to compile when I change my rule to something like: start = a_rule | *b_rule. The * and + operators cause the compiler to generate the following error: boost/spirit/home/qi/detail/assign_to.hpp:109: error: invalid static_cast from type ‘const std::vector<int, std::allocator<int> >’ to type ‘int'. Any ideas? Thanks, Vitaly
participants (2)
-
Hartmut Kaiser
-
Vitaly Budovski