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

Community
  • 1
  • 1
waffle
  • 121
  • 8

3 Answers3

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
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 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];` – waffle Apr 05 '15 at 09:12
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]]);
}
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • And significantly simplified for the case without a grammar class: **[Live On Coliru](http://coliru.stacked-crooked.com/a/9f5d9d402c6d8ecd)** – sehe Apr 05 '15 at 12:09
  • Cough. Now really simplified for the case without a grammar class: **[Live On Coliru](http://coliru.stacked-crooked.com/a/4653fea5843e1f06)** – sehe Apr 05 '15 at 13:12
  • 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... – waffle Apr 13 '15 at 09:26
  • 1
    Of course: **[Live On Coliru](http://coliru.stacked-crooked.com/a/03a6c37cd76e4c92)** See also [`BOOST_PHOENIX_ADAPT_FUNCTION`](http://www.boost.org/doc/libs/1_57_0/libs/phoenix/doc/html/phoenix/modules/function/adapting_functions.html#phoenix.modules.function.adapting_functions.boost_phoenix_adapt_function). In semantic actions, instead of `std::bind` or `boost::bind` use... `boost::phoenix::bind` :) – sehe Apr 13 '15 at 09:34
  • Thanks a lot. I was searching for something that was lighter (ie, avoiding a template), but at least this works :D – waffle Apr 13 '15 at 09:44
  • 1
    @waffle adapting the function gives you that: See it **[Live On Coliru](http://coliru.stacked-crooked.com/a/597d0c5712c5339d) too**. My sample was generic, so I needed the template anyways. – sehe Apr 13 '15 at 09:55
  • I am trying to do something cleaner : I created `class mydata {public: int getsetmax(int b); private: int my_max}`. I tried to set `var_ = int_ [_val = bind(&Mydata::getsetmax, &m, _1]` (m is a reference to a mydata objet, we initialize the parser with `parser(Mydata& m)`), but it does not work, and I do not really see why (g++ says it cannot convert a big bind to an int assignation) : should I create a function doing the binding and then adapting the function ? – waffle Apr 18 '15 at 09:35
  • @waffle either [using `phx::function<>`](http://coliru.stacked-crooked.com/a/8d9f9a83cd2612af) or [using `phx::bind`](http://coliru.stacked-crooked.com/a/f9ca1b2c92be5f12). I don't know why you'd do this because it's more tedious and makes the parser non-reentrant. Stateful functors are always tricky. Why don't you use the `qi::locals<>`? – sehe Apr 18 '15 at 09:48
  • this is because I am trying something more difficult. I need to store data in an object, and get an integer that will represent the data in the parsed result. – waffle Apr 18 '15 at 09:56
  • a really basic example would be a class where i store a list of strings. To each string, I need to assign an integer, and give this integer back to the parser for other manipulations in the parsed result... – waffle Apr 18 '15 at 10:06
  • Welcome to the land of X/Y problems. Sounds like you want builder pattern or flyweight. Both of which can be a lot simpler. Separate your concerns. Consider posting a question about what you want to achieve instead of how you are trying it right now. – sehe Apr 18 '15 at 10:07
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

sehe
  • 374,641
  • 47
  • 450
  • 633