1

This code compiles and works as intended; an input like "{a;b}" is parsed and stored in a custom class

#include <vector>
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>

namespace t {
    using std::vector;
    using std::string;
    namespace qi = boost::spirit::qi;

    struct Block {
        Block() = default;
        Block(vector<string> const& s) : value(s) {}
        vector<string> value;
    };

    template <typename Iterator, typename Skipper=qi::space_type>
    struct G1 : qi::grammar<Iterator, Block(), Skipper> {

        template <typename T>
        using rule = qi::rule<Iterator, T, Skipper>;

        rule<Block()> start;
        G1() : G1::base_type(start, "G1") {
            start = qi::as<vector<string>>()[
                qi::lit('{')
                >> *(+(qi::char_ - ';') >> ';')
                >> '}'
            ];
        }
    };

    Block parse(string const input) {
        G1<string::const_iterator> g;
        Block result;
        phrase_parse(begin(input), end(input), g, qi::standard::space, result);
        return result;
    }
};

int main() {
    using namespace std;
    auto r = t::parse("{a;b;}");
    for (auto& s : r.value) {
        cout << s << endl;
    }
}

What I don't understand is why the as<> directive is needed; from what I can infer from the docs the synthesized attribute of the primitive parsers should be already a vector of strings.

I've read this article about attribute propagation and attribute compatibility, but I miss the big picture; what happens when the as directive is (not) used?

dvd
  • 1,014
  • 6
  • 12

1 Answers1

2

Otherwise

*(+(qi::char_ - ';') >> ';')

would just expose a std::vector<char> (each kleene-+ would append into the same attribute). As a rule of thumb, kleene-operators always directly pushback into the referenced attribute, which also implies that it expects that attribute to be of container type (the boost::spirit::traits::container_value<> trait is used to detect what the attribute of the repeated parser expression should convert to).

In this case, you might find fusion-adaptation with qi::as_string more elegant: Live On Coliru

struct Block {
    vector<string> value;
};

BOOST_FUSION_ADAPT_STRUCT(t::Block,(std::vector<std::string>,value))
// ...

start = 
    qi::lit('{')
    >> *qi::as_string [ +(qi::char_ - ';') >> ';' ]
    >> '}'
;

With this, beware of

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • You are right!, obviously. There is a rule of thumb for when use the as<> directive and when a semantic action? Your advice? – dvd Jun 27 '14 at 07:46
  • 2
    Instead, I'd split out a subrule if you want to help/influence attribute compatibility. I'd only employ semantic actions as a way to optimize or to manage temporary rule state ([Boost Spirit: “Semantic actions are evil”?](http://stackoverflow.com/questions/8259440/boost-spirit-semantic-actions-are-evil/8259585#8259585)) – sehe Jun 27 '14 at 08:00
  • 1
    Here's the demonstration of that [split subrule](http://coliru.stacked-crooked.com/a/dc5b5d07097a0556) idea – sehe Jun 27 '14 at 08:01