1

I'm trying to get a grasp of Spirit, meaning I'm a noob at it (hence expect lack of proper terminology in the below).

I have to parse this:

value1 = 10
value2 = 20
value3 = 30
value4 = 40

Order doesn't matter but each "value1" ... "value4" line must be present exactly once. This would be OK:

value1 = 10
value4 = 40
value2 = 20
value3 = 30

but this would not be OK (duplicated "value1"):

value1 = 10
value2 = 20
value3 = 30
value4 = 40
value1 = 10000

Nor this (missing "value4"):

value1 = 10
value2 = 20
value3 = 30

How could I achieve that with Spirit ?

Bonus question: what if the line "value3" is optional ?

MaX.

HornetMaX
  • 43
  • 5

1 Answers1

1

I'd use the permutation parser + an added validity check.

This way you don't have to write all the stuff, and can still achieve the constraints you want.

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/optional/optional_io.hpp>

struct X {
    boost::optional<int> value1;
    boost::optional<int> value2;
    boost::optional<int> value3;
    boost::optional<int> value4;
};

static bool is_valid(X const& x) { return x.value1 && x.value2 && x.value4; } // value3 is optional

BOOST_FUSION_ADAPT_STRUCT(X,
        (boost::optional<int>, value1)
        (boost::optional<int>, value2)
        (boost::optional<int>, value3)
        (boost::optional<int>, value4)
    )

int main() {
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;
    using It = std::string::const_iterator;

    qi::rule<It, X(), qi::space_type> grammar;
    grammar =
        (("value1" > qi::lit('=') > qi::int_) ^
         ("value2" > qi::lit('=') > qi::int_) ^
         ("value3" > qi::lit('=') > qi::int_) ^
         ("value4" > qi::lit('=') > qi::int_))
        >> qi::eoi
        >> qi::eps(phx::bind(is_valid, qi::_val))
        ;

    for (std::string const& input : {
            "value1 = 10\nvalue2 = 20\nvalue3 = 30\nvalue4 = 40\n",
            // Order doesn't matter but each value1 ... value4 line must be present exactly once. This would be OK:
            "value1 = 10\nvalue4 = 40\nvalue2 = 20\nvalue3 = 30\n",
            // But this would not be OK (duplicated value1):
            "value1 = 10\nvalue2 = 20\nvalue3 = 30\nvalue4 = 40\nvalue1 = 10000\n",
            // Nor this (missing value4):
            "value1 = 10\nvalue2 = 20\nvalue3 = 30\n",
            // value3 _is_ optional though:
            "value1 = 10\nvalue2 = 20\nvalue4 = 40\n",
        })
    {
        std::cout << "---------------------------------------------------------\n";
        std::cout << "Parsing '" << input << "'\n";
        auto f(input.begin()), l(input.end());
        X parsed;
        bool ok = phrase_parse(f, l, grammar, qi::space, parsed);

        if (ok) {
            std::cout << "Parsing succeeded: " << boost::fusion::as_vector(parsed) << "\n";
        } else {
            std::cout << "Parsing failed\n";
        }

        if (f!=l)
            std::cout << "Remaing input '" << std::string(f,l) << "'\n";
    }
}

Prints

---------------------------------------------------------
Parsing 'value1 = 10
value2 = 20
value3 = 30
value4 = 40
'
Parsing succeeded: ( 10  20  30  40)
---------------------------------------------------------
Parsing 'value1 = 10
value4 = 40
value2 = 20
value3 = 30
'
Parsing succeeded: ( 10  20  30  40)
---------------------------------------------------------
Parsing 'value1 = 10
value2 = 20
value3 = 30
value4 = 40
value1 = 10000
'
Parsing failed
Remaing input 'value1 = 10
value2 = 20
value3 = 30
value4 = 40
value1 = 10000
'
---------------------------------------------------------
Parsing 'value1 = 10
value2 = 20
value3 = 30
'
Parsing failed
Remaing input 'value1 = 10
value2 = 20
value3 = 30
'
---------------------------------------------------------
Parsing 'value1 = 10
value2 = 20
value4 = 40
'
Parsing succeeded: ( 10  20 --  40)

As you can see, the last one addresses your bonus question.

Have a look at the keyword parser directive from the Spirit Repository too:

It allows you to set min/max occurrences

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks sehe, the keyword directive seems to fit the bill. In your solution however, any item can be optional, while I was more interested in having just one of them optional. Am I right ? – HornetMaX Feb 01 '15 at 19:12
  • @HometMaX Have you tried it? I think I've said exactly why I would do it this way :) Anyways, you're free to use the keyword parser (I don't have much experience with that, I just know it exists. It's not part of the supported Qi interfaces) – sehe Feb 01 '15 at 19:39
  • Hi sehe, no I didn't try it yet. I'd like to be 100% sure I understand it before writing it down (because the real file I have to parse is more complex than the example above). I'm also a bit scared by the usage of Fusion and Phoenix (that I don't know at all): still pondering if it's worth to learn them or if I can get this done without.Not sure I get what "not part of the supported Qi interfaces" means though. Thanks for the help ! – HornetMaX Feb 02 '15 at 07:40
  • Cheers! That's ok. I'd say if you're gonna use Spirit then learning Fusion and Phoenix is worth it (or vice versa). You can of course use the heavier guns (custom directives and customization points) but this is /a lot more/ complicated. My ["official" stance is to avoid semantic actions](http://stackoverflow.com/questions/8259440/boost-spirit-semantic-actions-are-evil) as much as possible, so I can assure you I can do most tasks in Spirit _without_ them. – sehe Feb 02 '15 at 07:43