2

I want to create the grammar that parses a list of key-values pairs, but accepts only a given keys. If the input list contains unknown keys the grammar should fail. The keys of "good" keys can be passed to grammar as qi::symbol table.

The question: is it possible to pass the keys as inherited attribute of the grammar?

I create the code prototype, but do not know how to convert the attribute info the parser or rule or subgrammar inside the grammar.

#include <iostream>
#include <vector>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>

using namespace std;
using namespace boost;

namespace qi = boost::spirit::qi;

template <typename I> using string_range = iterator_range<I>;

template <typename I> using pair_type = pair<
  boost::optional<int>,
  boost::optional<string_range<I>>
>;

template <typename I> using pairs_type = vector<pair_type<I>>;

using symbol_table = qi::symbols<char, int>;

template <typename Iterator>
struct keys_and_values
  : qi::grammar<Iterator, pairs_type<Iterator>(symbol_table const&)>
{
  keys_and_values()
    : keys_and_values::base_type(query)
  {
    using namespace qi;
    query =  pair (_r1) >> *((qi::lit(';') | '&') >> pair (_r1));
// How to convert the attribute into the parsing object???
    pair  =  -matches[_r1] >> -('=' >> -value);
    value =  raw[+qi::char_("a-zA-Z_0-9")];
  }
  qi::rule<Iterator, pairs_type<Iterator>(symbol_table const&)> query;
  qi::rule<Iterator, pair_type<Iterator>(symbol_table const&)> pair;
  qi::rule<Iterator, string_range<Iterator>()> value;
};

int main ()
{
  namespace qi = boost::spirit::qi;
  string input { "key1=v1;key2=v2;key3=v3" };

  using string_iterator = string::const_iterator;

  static const keys_and_values <string_iterator> p;
  pairs_type <string_iterator> m;

  symbol_table keys;
  keys.add ("key1", 1) ("key2", 2) ("key3", 3) ;

  string_iterator begin{boost::begin (input)}, end{boost::end(input)};

  if (qi::parse (begin, end, p(boost::phoenix::ref(keys)), m))
    cout << "parse ok\n";
}

A link to the same code on coilru.

Nikki Chumakov
  • 1,215
  • 8
  • 18

1 Answers1

3

Sure. This is closely related to the famed Nabialek Trick.

And the enabling mechanism is qi::lazy:

    pair  =  -matches[lazy(_r1)] >> -('=' >> -value);

I also add #define BOOST_SPIRIT_USE_PHOENIX_V3 (which you may not explicitly have to set, depending on compiler/boost version).

Live On Coliru

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <vector>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>

using namespace std;
using namespace boost;

namespace qi = boost::spirit::qi;

template <typename I> using string_range = iterator_range<I>;

template <typename I> using pair_type = pair<
    boost::optional<int>,
    boost::optional<string_range<I>>
>;

template <typename I> using pairs_type = vector<pair_type<I>>;

using symbol_table = qi::symbols<char, int>;

template <typename Iterator>
struct keys_and_values
    : qi::grammar<Iterator, pairs_type<Iterator>(symbol_table const&)>
{
    keys_and_values()
    : keys_and_values::base_type(query)
    {
        using namespace qi;
        query =  pair (_r1) >> *((qi::lit(';') | '&') >> pair (_r1));
        // How to convert the attribute into the parsing object???
        pair  =  -matches[lazy(_r1)] >> -('=' >> -value);
        value =  raw[+qi::char_("a-zA-Z_0-9")];
    }
    qi::rule<Iterator, pairs_type<Iterator>(symbol_table const&)> query;
    qi::rule<Iterator, pair_type<Iterator>(symbol_table const&)> pair;
    qi::rule<Iterator, string_range<Iterator>()> value;
};

int main ()
{
    namespace qi = boost::spirit::qi;
    string input { "key1=v1;key2=v2;key3=v3" };

    using string_iterator = string::const_iterator;

    static const keys_and_values <string_iterator> p;
    pairs_type <string_iterator> m;

    symbol_table keys;
    keys.add ("key1", 1) ("key2", 2) ("key3", 3) ;

    string_iterator begin{boost::begin (input)}, end{boost::end(input)};

    if (qi::parse (begin, end, p(boost::phoenix::ref(keys)), m))
        cout << "parse ok\n";
}

Output:

parse ok
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank you with great answer! Now I have related (but different) problem, please take a look if you have a time at http://stackoverflow.com/questions/26414677 – Nikki Chumakov Oct 16 '14 at 22:06