Reputation: 441
Boost Spirit qi::symbols implements a map of key-value pairs: give a key of a string, it can return a certain value. My questions are:
1) For a blank string, is it possible to return a default value? (Q1 in code)
2) For a string that is other than a blank string or the keys listed in the map of key-value pairs, is it possible to return a value indicating the key is invalid? (Q2 in code)
** The following code is based on the BOOST SPIRIT document. ** Thanks in advance for any suggestions.
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/assert.hpp>
#include <iostream>
#include <string>
#include <cstdlib>
template <typename P, typename T>
void test_parser_attr(
char const* input, P const& p, T& attr, bool full_match = true)
{
using boost::spirit::qi::parse;
char const* f(input);
char const* l(f + strlen(f));
if (parse(f, l, p, attr) && (!full_match || (f == l)))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
int main()
{
using boost::spirit::qi::symbols;
symbols<char, int> sym;
sym.add
("Apple", 1)
("Banana", 2)
("Orange", 3)
;
int i;
test_parser_attr("Banana", sym, i);
std::cout << i << std::endl; // 2
test_parser_attr("", sym, i); // Q1: key is "",
std::cout << i << std::endl; // would like it to be 1 as default
test_parser_attr("XXXX", sym, i); // Q2: key is other than "Apple"/"Banana"/"Orange",
std::cout << i << std::endl; // would like it to be 4
return 0;
}
Upvotes: 4
Views: 442
Reputation: 155
You can't accomplish what you want using just a qi::symbols
. It should be possible to create a Spirit terminal/directive that achieves the desired result but doing so would be really complex and require knowledge of the inner workings of qi::symbols
and related classes, so I don't think it's a worthwhile approach. Luckily there is a really simple alternative using qi::attr(val)
, a parser that doesn't consume any input, exposes val
as its attribute and always succeeds.
Let's see all three cases:
->
just use sym
->
just use attr(1)
->
here you need to use attr(4)
but that isn't enough since you also need to consume the string. If we assume the string is composed of only letters omit[+alpha]
could work (omit
discards the text, and +
makes sure that there is at least one letter).You need to put these three parsers in an alternative parser keeping in mind that the parser actually used in each case will be the first one to succeed:
sym | omit[+alpha] >> attr(4) | attr(1)
Possible problems:
+alpha
appropriately.omit[
lexeme
[+alpha]]
to stop the greedy +
.Full Sample (Running on WandBox)
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
template <typename P, typename T>
void test_parser_attr(
char const* input, P const& p, T& attr, bool full_match = true)
{
using boost::spirit::qi::parse;
char const* f(input);
char const* l(f + strlen(f));
if (parse(f, l, p, attr) && (!full_match || (f == l)))
std::cout << "ok" << std::endl;
else
std::cout << "fail" << std::endl;
}
int main()
{
using boost::spirit::qi::symbols;
symbols<char, int> sym;
sym.add
("Apple", 1)
("Banana", 2)
("Orange", 3)
;
using boost::spirit::qi::attr;
using boost::spirit::qi::alpha;
using boost::spirit::qi::omit;
using boost::spirit::qi::lexeme;
//if the text is in the symbol table return the associated value
//else if the text is formed by letters (you could change this using for example `alnum`) and contains at least one, discard the text and return 4
//else return 1
boost::spirit::qi::rule<char const*,int()> symbols_with_defaults = sym | omit[+alpha] >> attr (4) | attr(1);
int i;
test_parser_attr("Banana", symbols_with_defaults, i);
std::cout << i << std::endl; // 2
test_parser_attr("", symbols_with_defaults, i); // Q1: key is "",
std::cout << i << std::endl; // would like it to be 1 as default
test_parser_attr("XXXX", symbols_with_defaults, i); // Q2: key is other than "Apple"/"Banana"/"Orange",
std::cout << i << std::endl; // would like it to be 4
std::vector<int> values;
test_parser_attr("<Banana>,<>,<xxxx>", ('<' >> symbols_with_defaults >> '>')%',', values);
for(int val : values)
std::cout << val << " "; // should be '2 1 4'
std::cout << std::endl;
return 0;
}
Upvotes: 3