1

I'm trying the code sehe gave here : Boolean expression (grammar) parser in c++

I would like to create a string variable max, that would store the maximum variable encountered at each parsing (on the lexicographic order, for example).

I tried things like :

  1. var_ = qi::lexeme[ +alpha ] [_val = _1, if_(phx::ref(m) < _1) [phx::ref(m) = _1]];, but there is a (really long) compilation error

  2. var_ = qi::lexeme[ +alpha [_val = _1, if_(phx::ref(m) < _1) [phx::ref(m) = _1]]]; but with this one I only get the first caracter of a variable, which is restrincting.

I also tried to simplify things using integers instead of string for variables, but var_ = int_ [...] didn't work either, because int_ is already a parser (I think).

Do you have any ideas ?

Thanks in advance

1
  • can you make the sample self-contained? Commented Apr 5, 2015 at 0:13

3 Answers 3

1

I'd say that

start = *word [ if_(_1>_val) [_val=_1] ]; 

should be fine. However, due to a bug (?) Phoenix statements in a single-statement semantic action do not compile. You can easily work around it using a no-op statement, like e.g. _pass=true in this context:

start = *word [ if_(_1>_val) [_val=_1], _pass = true ]; 

Now, for this I assumed a

rule<It, std::string()> word = +alpha; 

If you insist you can cram it all into one rule though:

start = *as_string[lexeme[+alpha]] [ if_(_1>_val) [_val=_1], _pass = true ]; 

I don't recommend that.

Demo

Live On Colir

#define BOOST_SPIRIT_USE_PHOENIX_V3 #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; namespace phx = boost::phoenix; template <typename It, typename Skipper> struct max_parser : qi::grammar<It, std::string(), Skipper> { max_parser() : max_parser::base_type(start) { using namespace qi; using phx::if_; #if 1 word = lexeme [ +alpha ]; start = *word [ if_(_1>_val) [_val=_1], _pass = true ]; #else start = *as_string[lexeme[+alpha]] [ if_(_1>_val) [_val=_1], _pass = true ]; #endif } private: qi::rule<It, std::string(), Skipper> start, word; }; int main() { std::string const input("beauty shall be in ze eye of the beholder"); using It = std::string::const_iterator; max_parser<It, qi::space_type> parser; std::string data; It it = input.begin(), end = input.end(); bool ok = qi::phrase_parse(it, end, parser, qi::space, data); if (ok) { std::cout << "Parse success: " << data << "\n"; } else { std::cout << "Parse failed\n"; } if (it != end) std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n"; } 

Prints:

Parse success: ze 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answers. I wanted to do both usual parsing and keeping the maximum encountered string, and it worked with : var_ = *as_string[qi::lexeme[ +digit ]] [if_(phx::ref(m) < _1) [phx::ref(m) = _1], _val = _1];
1

Re: comment:

Thanks for your answers. I wanted to do both usual parsing and keeping the maximum encountered string, and it worked with : var_ = *as_string[qi::lexeme[ +digit ]] [if_(phx::ref(m) < _1) [phx::ref(m) = _1], _val = _1];

For even more fun, and in the interest of complete overkill, I've come up with something that I think is close to useful:

Live On Coliru

int main() { do_test<int>(" 1 99 -1312 4 1014", -9999); do_test<double>(" 1 NaN -4 7e3 7e4 -31e9"); do_test<std::string>("beauty shall be in ze eye of the beholder", "", qi::as_string[qi::lexeme[+qi::graph]]); } 

The sample prints:

Parse success: 5 elements with maximum of 1014 values: 1 99 -1312 4 1014 Parse success: 6 elements with maximum of 70000 values: 1 nan -4 7000 70000 -3.1e+10 Parse success: 9 elements with maximum of ze values: beauty shall be in ze eye of the beholder 

As you can see, with string we need to help the Spirit a bit because it doesn't know how you would like to "define" a single "word". The test driver is completely generic:

template <typename T, typename ElementParser = typename boost::spirit::traits::create_parser<T>::type> void do_test(std::string const& input, T const& start_value = std::numeric_limits<T>::lowest(), ElementParser const& element_parser = boost::spirit::traits::create_parser<T>::call()) { using It = std::string::const_iterator; vector_and_max<T> data; It it = input.begin(), end = input.end(); bool ok = qi::phrase_parse(it, end, max_parser<It, T>(start_value, element_parser), qi::space, data); if (ok) { std::cout << "Parse success: " << data.first.size() << " elements with maximum of " << data.second << "\n"; std::copy(data.first.begin(), data.first.end(), std::ostream_iterator<T>(std::cout << "\t values: ", " ")); std::cout << "\n"; } else { std::cout << "Parse failed\n"; } if (it != end) std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n"; } 

The start-element and element-parser are passed to the constructor of our grammar:

template <typename T> using vector_and_max = std::pair<std::vector<T>, T>; template <typename It, typename T, typename Skipper = qi::space_type> struct max_parser : qi::grammar<It, vector_and_max<T>(), Skipper> { template <typename ElementParser> max_parser(T const& start_value, ElementParser const& element_parser) : max_parser::base_type(start) { using namespace qi; using phx::if_; _a_type running_max; vector_with_max %= eps [ running_max = start_value ] >> *boost::proto::deep_copy(element_parser) [ if_(_1>running_max) [running_max=_1], _pass = true ] >> attr(running_max) ; start = vector_with_max; } private: qi::rule<It, vector_and_max<T>(), Skipper> start; qi::rule<It, vector_and_max<T>(), Skipper, qi::locals<T> > vector_with_max; }; 

Full Listing

For reference

Live On Coliru

#define BOOST_SPIRIT_USE_PHOENIX_V3 #include <boost/fusion/adapted/std_pair.hpp> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace qi = boost::spirit::qi; namespace phx = boost::phoenix; template <typename T> using vector_and_max = std::pair<std::vector<T>, T>; template <typename It, typename T, typename Skipper = qi::space_type> struct max_parser : qi::grammar<It, vector_and_max<T>(), Skipper> { template <typename ElementParser> max_parser(T const& start_value, ElementParser const& element_parser) : max_parser::base_type(start) { using namespace qi; using phx::if_; _a_type running_max; vector_with_max %= eps [ running_max = start_value ] >> *boost::proto::deep_copy(element_parser) [ if_(_1>running_max) [running_max=_1], _pass = true ] >> attr(running_max) ; start = vector_with_max; } private: qi::rule<It, vector_and_max<T>(), Skipper> start; qi::rule<It, vector_and_max<T>(), Skipper, qi::locals<T> > vector_with_max; }; template <typename T, typename ElementParser = typename boost::spirit::traits::create_parser<T>::type> void do_test(std::string const& input, T const& start_value = std::numeric_limits<T>::lowest(), ElementParser const& element_parser = boost::spirit::traits::create_parser<T>::call()) { using It = std::string::const_iterator; vector_and_max<T> data; It it = input.begin(), end = input.end(); bool ok = qi::phrase_parse(it, end, max_parser<It, T>(start_value, element_parser), qi::space, data); if (ok) { std::cout << "Parse success: " << data.first.size() << " elements with maximum of " << data.second << "\n"; std::copy(data.first.begin(), data.first.end(), std::ostream_iterator<T>(std::cout << "\t values: ", " ")); std::cout << "\n"; } else { std::cout << "Parse failed\n"; } if (it != end) std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n"; } int main() { do_test<int>(" 1 99 -1312 4 1014"); do_test<double>(" 1 NaN -4 7e3 7e4 -31e9"); do_test<std::string>("beauty shall be in ze eye of the beholder", "", qi::as_string[qi::lexeme[+qi::graph]]); } 

11 Comments

And significantly simplified for the case without a grammar class: Live On Coliru
Cough. Now really simplified for the case without a grammar class: Live On Coliru
I have one more question : is it possible to do the same, but to call a function computing the maximum and returning the good value to val ? Instead of var_ = int_ [_val = _1, if_(phx::ref(m) < _1) [phx::ref(m) = _1]];, I would like to do : var_ = int_[_val = magic(_1)] with int magic(int& i) {if(i > m) i = m; return i;} for example ? I tried also with std::bind, but nothing seems to work : I encountered problems with the types (_1-related type vs int), or the binding itself...
Of course: Live On Coliru See also BOOST_PHOENIX_ADAPT_FUNCTION. In semantic actions, instead of std::bind or boost::bind use... boost::phoenix::bind :)
@waffle adapting the function gives you that: See it Live On Coliru too. My sample was generic, so I needed the template anyways.
|
0

Just for fun, here's how to do roughly¹ the same as in my other answer, and more, but without using boost spirit at all:

Live On Coliru

#include <algorithm> #include <sstream> #include <iterator> #include <iostream> int main() { std::istringstream iss("beauty shall be in ze eye of the beholder"); std::string top2[2]; auto end = std::partial_sort_copy( std::istream_iterator<std::string>(iss), {}, std::begin(top2), std::end(top2), std::greater<std::string>()); for (auto it=top2; it!=end; ++it) std::cout << "(Next) highest word: '" << *it << "'\n"; } 

Output:

(Next) highest word: 'ze' (Next) highest word: 'the' 

¹ we're not nearly as specific about isalpha and isspace character types here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.