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.
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).
#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