So I have this, rather ugly rule in my boost::spirit parser;
qi::rule<Iterator, bool()> empty_brackets;
...
empty_brackets = (qi::token(LEFT_BRACKET) >> qi::token(RIGHT_BRACKET))
[ qi::_val = true ]
;
The rule is used as;
array_typeexp = (primitive_typeexp >> +(empty_brackets))
So what I really want, for the second argument (qi::_2), isn't a std::vector<bool>, but rather an unsigned int, describing the number of times 'empty_brackets' was matched.
How would I go about achieving this?
I'm really unsure what you could need this for, but, hey :/
I'd suggest
empty_brackets = lit('(') >> lit(')');
n_empty_brackets = eps [ _val = 0 ] >> +empty_brackets [ ++_val ];
array_typeexp = primitive_typeexp >> n_empty_brackets;
For simplicity I have made primitive_typeexp a "no-op", and therefore the outermost rules return just the unsigned value in this simple setup:
qi::rule<It, unsigned(), Skipper> array_typeexp, n_empty_brackets;
qi::rule<It, Skipper> empty_brackets, primitive_typeexp;
See a complete sample Live on Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, unsigned(), Skipper>
{
parser() : parser::base_type(array_typeexp)
{
using namespace qi;
empty_brackets = lit('(') >> lit(')');
n_empty_brackets = eps [ _val = 0 ] >> +empty_brackets [ ++_val ];
array_typeexp = primitive_typeexp >> n_empty_brackets;
// TODO
primitive_typeexp = eps;
BOOST_SPIRIT_DEBUG_NODES((array_typeexp)(n_empty_brackets)(empty_brackets)(primitive_typeexp));
}
private:
qi::rule<It, unsigned(), Skipper> array_typeexp, n_empty_brackets;
qi::rule<It, Skipper> empty_brackets, primitive_typeexp;
};
bool doParse(const std::string& input)
{
typedef std::string::const_iterator It;
auto f(begin(input)), l(end(input));
parser<It, qi::space_type> p;
unsigned data;
try
{
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
std::cout << "data: " << data << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
} catch(const qi::expectation_failure<It>& e)
{
std::string frag(e.first, e.last);
std::cerr << e.what() << "'" << frag << "'\n";
}
return false;
}
int main()
{
doParse("() () ( ) ()");
doParse("()");
}
Related
I'm trying to implement a very specific grammar, which requires me at a certain point to parse a list of comma separated integers. The qi rule looks like the following:
qi::rule<Iterator, ascii::space_type> ident;
qi::rule<Iterator, ascii::space_type> nlist;
...
ident = char_ >> nlist;
nlist = ("(" >> int_ % "," >> ")");
...
I need to pass the values up to the ident rule (The expression ident has to create a syntax tree node, where the parsed values from nlist are required for the constructor). I thought about creating and filling a std::vector and use the semantic action like _val = vector<int>.... What is now unclear to me is how do I create a vector of arbitrary length from this rule, since I do not make any assumptions on how long the input will be or using a predefined vector like the examples.
Is this even possible or does is there a better way to do it?
This is the bread and butter of Spirit Qi.
Just use any compatible attribute type and profit:
using nlist_t = std::vector<int>;
using ident_t = std::pair<char, nlist_t>;
qi::rule<Iterator, ident_t(), qi::ascii::space_type> ident;
qi::rule<Iterator, nlist_t(), qi::ascii::space_type> nlist;
Note: For std::pair attribute compatibility, include the relevant fusion header:
Live On Coliru
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main()
{
using nlist_t = std::vector<int>;
using ident_t = std::pair<char, nlist_t>;
using Iterator = std::string::const_iterator;
qi::rule<Iterator, ident_t(), qi::ascii::space_type> ident;
qi::rule<Iterator, nlist_t(), qi::ascii::space_type> nlist;
ident = qi::char_ >> nlist;
nlist = '(' >> qi::int_ % ',' >> ')';
for (std::string const input : { "a (1,2,3)", "+(881,-2,42) \n", "?(0)" }) {
ident_t data;
if (qi::phrase_parse(input.begin(), input.end(), ident, qi::ascii::space, data)) {
std::cout << "Parsed: " << data.first << "(";
for (auto i : data.second) std::cout << i << ",";
std::cout << ")\n";
} else
std::cout << "Parse failed: '" << input << "'\n";
}
}
Prints
Parsed: a(1,2,3,)
Parsed: +(881,-2,42,)
Parsed: ?(0,)
BONUS
Version with imagined Ast type using phoenix::construct:
Also Live On Coliru
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace OoShinyAst {
using MyName = char;
using MyArgument = int;
using MyArgumentList = std::vector<MyArgument>;
struct MyIdent {
MyName name;
MyArgumentList args;
MyIdent() = default;
MyIdent(MyName name, MyArgumentList args)
: name(std::move(name)), args(std::move(args)) { }
};
}
int main()
{
using Iterator = std::string::const_iterator;
qi::rule<Iterator, OoShinyAst::MyIdent(), qi::ascii::space_type> ident;
qi::rule<Iterator, OoShinyAst::MyArgumentList(), qi::ascii::space_type> nlist;
nlist = '(' >> qi::int_ % ',' >> ')';
ident = (qi::char_ >> nlist) [ qi::_val = px::construct<OoShinyAst::MyIdent>(qi::_1, qi::_2) ];
for (std::string const input : { "a (1,2,3)", "+(881,-2,42) \n", "?(0)" }) {
OoShinyAst::MyIdent data;
if (qi::phrase_parse(input.begin(), input.end(), ident, qi::ascii::space, data)) {
std::cout << "Parsed: " << data.name << "(";
for (auto i : data.args) std::cout << i << ",";
std::cout << ")\n";
} else
std::cout << "Parse failed: '" << input << "'\n";
}
}
I'm trying the code sehe gave here : Boolean expression (grammar) parser in c++
I would like to create a string variable max, that would store the maximum variable encountered at each parsing (on the lexicographic order, for example).
I tried things like :
var_ = qi::lexeme[ +alpha ] [_val = _1, if_(phx::ref(m) < _1) [phx::ref(m) = _1]];, but there is a (really long) compilation error
var_ = qi::lexeme[ +alpha [_val = _1, if_(phx::ref(m) < _1) [phx::ref(m) = _1]]]; but with this one I only get the first caracter of a variable, which is restrincting.
I also tried to simplify things using integers instead of string for variables, but var_ = int_ [...] didn't work either, because int_ is already a parser (I think).
Do you have any ideas ?
Thanks in advance
I'd say that
start = *word [ if_(_1>_val) [_val=_1] ];
should be fine. However, due to a bug (?) Phoenix statements in a single-statement semantic action do not compile. You can easily work around it using a no-op statement, like e.g. _pass=true in this context:
start = *word [ if_(_1>_val) [_val=_1], _pass = true ];
Now, for this I assumed a
rule<It, std::string()> word = +alpha;
If you insist you can cram it all into one rule though:
start = *as_string[lexeme[+alpha]] [ if_(_1>_val) [_val=_1], _pass = true ];
I don't recommend that.
Demo
Live On Colir
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
template <typename It, typename Skipper>
struct max_parser : qi::grammar<It, std::string(), Skipper> {
max_parser() : max_parser::base_type(start) {
using namespace qi;
using phx::if_;
#if 1
word = lexeme [ +alpha ];
start = *word [ if_(_1>_val) [_val=_1], _pass = true ];
#else
start = *as_string[lexeme[+alpha]] [ if_(_1>_val) [_val=_1], _pass = true ];
#endif
}
private:
qi::rule<It, std::string(), Skipper> start, word;
};
int main() {
std::string const input("beauty shall be in ze eye of the beholder");
using It = std::string::const_iterator;
max_parser<It, qi::space_type> parser;
std::string data;
It it = input.begin(), end = input.end();
bool ok = qi::phrase_parse(it, end, parser, qi::space, data);
if (ok) {
std::cout << "Parse success: " << data << "\n";
} else {
std::cout << "Parse failed\n";
}
if (it != end)
std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n";
}
Prints:
Parse success: ze
Re: comment:
Thanks for your answers. I wanted to do both usual parsing and keeping the maximum encountered string, and it worked with : var_ = *as_string[qi::lexeme[ +digit ]] [if_(phx::ref(m) < _1) [phx::ref(m) = _1], _val = _1];
For even more fun, and in the interest of complete overkill, I've come up with something that I think is close to useful:
Live On Coliru
int main() {
do_test<int>(" 1 99 -1312 4 1014", -9999);
do_test<double>(" 1 NaN -4 7e3 7e4 -31e9");
do_test<std::string>("beauty shall be in ze eye of the beholder", "", qi::as_string[qi::lexeme[+qi::graph]]);
}
The sample prints:
Parse success: 5 elements with maximum of 1014
values: 1 99 -1312 4 1014
Parse success: 6 elements with maximum of 70000
values: 1 nan -4 7000 70000 -3.1e+10
Parse success: 9 elements with maximum of ze
values: beauty shall be in ze eye of the beholder
As you can see, with string we need to help the Spirit a bit because it doesn't know how you would like to "define" a single "word". The test driver is completely generic:
template <typename T, typename ElementParser = typename boost::spirit::traits::create_parser<T>::type>
void do_test(std::string const& input,
T const& start_value = std::numeric_limits<T>::lowest(),
ElementParser const& element_parser = boost::spirit::traits::create_parser<T>::call())
{
using It = std::string::const_iterator;
vector_and_max<T> data;
It it = input.begin(), end = input.end();
bool ok = qi::phrase_parse(it, end, max_parser<It, T>(start_value, element_parser), qi::space, data);
if (ok) {
std::cout << "Parse success: " << data.first.size() << " elements with maximum of " << data.second << "\n";
std::copy(data.first.begin(), data.first.end(), std::ostream_iterator<T>(std::cout << "\t values: ", " "));
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (it != end)
std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n";
}
The start-element and element-parser are passed to the constructor of our grammar:
template <typename T>
using vector_and_max = std::pair<std::vector<T>, T>;
template <typename It, typename T, typename Skipper = qi::space_type>
struct max_parser : qi::grammar<It, vector_and_max<T>(), Skipper> {
template <typename ElementParser>
max_parser(T const& start_value, ElementParser const& element_parser) : max_parser::base_type(start) {
using namespace qi;
using phx::if_;
_a_type running_max;
vector_with_max %=
eps [ running_max = start_value ]
>> *boost::proto::deep_copy(element_parser)
[ if_(_1>running_max) [running_max=_1], _pass = true ]
>> attr(running_max)
;
start = vector_with_max;
}
private:
qi::rule<It, vector_and_max<T>(), Skipper> start;
qi::rule<It, vector_and_max<T>(), Skipper, qi::locals<T> > vector_with_max;
};
Full Listing
For reference
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
template <typename T>
using vector_and_max = std::pair<std::vector<T>, T>;
template <typename It, typename T, typename Skipper = qi::space_type>
struct max_parser : qi::grammar<It, vector_and_max<T>(), Skipper> {
template <typename ElementParser>
max_parser(T const& start_value, ElementParser const& element_parser) : max_parser::base_type(start) {
using namespace qi;
using phx::if_;
_a_type running_max;
vector_with_max %=
eps [ running_max = start_value ]
>> *boost::proto::deep_copy(element_parser)
[ if_(_1>running_max) [running_max=_1], _pass = true ]
>> attr(running_max)
;
start = vector_with_max;
}
private:
qi::rule<It, vector_and_max<T>(), Skipper> start;
qi::rule<It, vector_and_max<T>(), Skipper, qi::locals<T> > vector_with_max;
};
template <typename T, typename ElementParser = typename boost::spirit::traits::create_parser<T>::type>
void do_test(std::string const& input,
T const& start_value = std::numeric_limits<T>::lowest(),
ElementParser const& element_parser = boost::spirit::traits::create_parser<T>::call())
{
using It = std::string::const_iterator;
vector_and_max<T> data;
It it = input.begin(), end = input.end();
bool ok = qi::phrase_parse(it, end, max_parser<It, T>(start_value, element_parser), qi::space, data);
if (ok) {
std::cout << "Parse success: " << data.first.size() << " elements with maximum of " << data.second << "\n";
std::copy(data.first.begin(), data.first.end(), std::ostream_iterator<T>(std::cout << "\t values: ", " "));
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (it != end)
std::cout << "Remaining unparsed: '" << std::string(it,end) << "'\n";
}
int main() {
do_test<int>(" 1 99 -1312 4 1014");
do_test<double>(" 1 NaN -4 7e3 7e4 -31e9");
do_test<std::string>("beauty shall be in ze eye of the beholder", "", qi::as_string[qi::lexeme[+qi::graph]]);
}
Just for fun, here's how to do roughly¹ the same as in my other answer, and more, but without using boost spirit at all:
Live On Coliru
#include <algorithm>
#include <sstream>
#include <iterator>
#include <iostream>
int main() {
std::istringstream iss("beauty shall be in ze eye of the beholder");
std::string top2[2];
auto end = std::partial_sort_copy(
std::istream_iterator<std::string>(iss), {},
std::begin(top2), std::end(top2),
std::greater<std::string>());
for (auto it=top2; it!=end; ++it)
std::cout << "(Next) highest word: '" << *it << "'\n";
}
Output:
(Next) highest word: 'ze'
(Next) highest word: 'the'
¹ we're not nearly as specific about isalpha and isspace character types here
I want to parse a file containing the following structure:
some
garbage *&%
section1 {
section_content
}
section2 {
section_content
}
The rule parsing section_name1 { ... } section_name2 { ... } is already defined:
section_name_rule = lexeme[+char_("A-Za-z0-9_")];
section = section_name_rule > lit("{") > /*some complicated things*/... > lit("}");
sections %= +section;
So I need to skip any garbage until the sections rule is met.
Is there any way to accomplish this? I have tried seek[sections], but it seems not to work.
EDIT:
I localized the reason why seek is not working: if I use follows operator(>>), then it works. If expectation parser is used (>), then it throws an exception. Here is a sample code:
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
using boost::phoenix::push_back;
struct section_t {
std::string name, contents;
friend std::ostream& operator<<(std::ostream& os, section_t const& s) { return os << "section_t[" << s.name << "] {" << s.contents << "}"; }
};
BOOST_FUSION_ADAPT_STRUCT(section_t, (std::string, name)(std::string, contents))
typedef std::vector<section_t> sections_t;
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, sections_t(), Skipper>
{
grammar() : grammar::base_type(start) {
using namespace qi;
using boost::spirit::repository::qi::seek;
section_name_rule = lexeme[+char_("A-Za-z0-9_")];
//Replacing '>>'s with '>'s throws an exception, while this works as expected!!
section = section_name_rule
>>
lit("{") >> lexeme[*~char_('}')] >> lit("}");
start = seek [ hold[section[push_back(qi::_val, qi::_1)]] ]
>> *(section[push_back(qi::_val, qi::_1)]);
}
private:
qi::rule<It, sections_t(), Skipper> start;
qi::rule<It, section_t(), Skipper> section;
qi::rule<It, std::string(), Skipper> section_name_rule;
};
int main() {
typedef std::string::const_iterator iter;
std::string storage("sdfsdf\n sd:fgdfg section1 {dummy } section2 {dummy } section3 {dummy }");
iter f(storage.begin()), l(storage.end());
sections_t sections;
if (qi::phrase_parse(f, l, grammar<iter>(), qi::space, sections))
{
for(auto& s : sections)
std::cout << "Parsed: " << s << "\n";
}
if (f != l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
So in the real example my entire grammar is constructed with expectation operators. Do I have to change everything to make the "seek" work, or is there any other way (let's say, seek a simple "{", and revert one section_name_rule back)??
Here's a demonstration, using Hamlet for inspiration: Live On Coliru
start = *seek [ no_skip[eol] >> hold [section] ];
Notes:
drop the expectation points
optimize by requiring start of line before section name
Example input:
some
garbage *&%
section1 {
Claudius: ...But now, my cousin Hamlet, and my son —
Hamlet: A little more than kin, and less than kind.
}
WE CAN DO MOAR GARBAGE
section2 {
Claudius: How is it that the clouds still hang on you?
Hamlet: Not so my lord; I am too much i' the sun
}
Output:
Parsed: section_t[section1] {Claudius: ...But now, my cousin Hamlet, and my son —
Hamlet: A little more than kin, and less than kind.
}
Parsed: section_t[section2] {Claudius: How is it that the clouds still hang on you?
Hamlet: Not so my lord; I am too much i' the sun
}
Reference Listing
// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
namespace qi = boost::spirit::qi;
struct section_t {
std::string name, contents;
friend std::ostream& operator<<(std::ostream& os, section_t const& s) { return os << "section_t[" << s.name << "] {" << s.contents << "}"; }
};
BOOST_FUSION_ADAPT_STRUCT(section_t, (std::string, name)(std::string, contents))
typedef std::vector<section_t> sections_t;
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, sections_t(), Skipper>
{
grammar() : grammar::base_type(start) {
using namespace qi;
using boost::spirit::repository::qi::seek;
section_name_rule = lexeme[+char_("A-Za-z0-9_")];
section = section_name_rule >> '{' >> lexeme[*~char_('}')] >> '}';
start = *seek [ no_skip[eol] >> hold [section] ];
BOOST_SPIRIT_DEBUG_NODES((start)(section)(section_name_rule))
}
private:
qi::rule<It, sections_t(), Skipper> start;
qi::rule<It, section_t(), Skipper> section;
qi::rule<It, std::string(), Skipper> section_name_rule;
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
sections_t sections;
if (qi::phrase_parse(f, l, grammar<It>(), qi::space, sections))
{
for(auto& s : sections)
std::cout << "Parsed: " << s << "\n";
}
if (f != l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
There is a need of providing the values from an object of type boost::variant for an std::pair object. How would you implement this idea using other resources? Any other way than this is done below?
struct aggr_pair_visitor : public ::boost::static_visitor<void>
{
public:
explicit aggr_pair_visitor( column_and_aggregate & pair_ ) : pair(pair_)
{
}
void operator()(column_name_t const & column)
{
pair.first = column;
}
void operator()(unsigned const & faggr)
{
if ( faggr > static_cast<unsigned>(sql_faggregate::SUM) || faggr < static_cast<unsigned>(sql_faggregate::AVG) )
throw std::runtime_error("Failed to parse aggregate type : Not valid integer");
else pair.second = static_cast<sql_faggregate>(faggr);
}
private:
column_and_aggregate & pair;
};
void apply_col_and_aggr_visitor( column_and_aggregate & col_and_aggr_pair, ::boost::variant< column_name_t, unsigned > const & val )
{
aggr_pair_visitor pair_visitor( col_and_aggr_pair );
::boost::apply_visitor( pair_visitor, val ); // N.B.!!! Runtime execution of operator()!
}
spirit::qi::rule< iterator, column_and_aggregate(), ascii::space_type > agg_pair =
quoted_string[::boost::bind( &apply_col_and_aggr_visitor, spirit::qi::_val, spirit::qi::_1 )]
> ':'
> spirit::int_[::boost::bind( &apply_col_and_aggr_visitor, spirit::qi::_val, spirit::qi::_1 )];
spirit::qi::rule< iterator, column_and_aggregate_container(), ascii::space_type > aggregates_parser =
'{'
> agg_pair[phoenix::push_back(spirit::qi::_val, spirit::qi::_1)] % ',' // N.B.!!! list-redux technic
> '}';
Okay, on second glance I think you just missed the ability to fusion adapt std::pair:
#include <boost/fusion/adapted/std_pair.hpp>
// Or:
#include <boost/fusion/adapted.hpp>
Using this, the whole complexity vanishes and there is no need for anything involving the variant. Let's assume the following types:
typedef std::string column_name_t;
enum sql_faggregate
{
SUM,
// ....
AVG,
};
typedef std::pair<column_name_t, sql_faggregate> column_and_aggregate;
typedef std::vector<column_and_aggregate> column_and_aggregate_container;
A simple grammar would be:
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, column_and_aggregate_container(), Skipper>
{
parser() : parser::base_type(aggregates_parser)
{
using namespace qi;
// using phx::bind; using phx::ref; using phx::val;
quoted_string = lexeme [ "'" >> *~qi::char_("'") >> "'" ];
faggr = int_;
agg_pair = quoted_string > ':' > faggr;
aggregates_parser = '{' > agg_pair % ',' > '}';
}
private:
qi::rule<It, std::string(), qi::space_type> quoted_string;
qi::rule<It, sql_faggregate(), qi::space_type> faggr;
qi::rule<It, column_and_aggregate(), qi::space_type> agg_pair;
qi::rule<It, column_and_aggregate_container(), qi::space_type> aggregates_parser;
};
You could add the input validation too:
faggr %= int_ [ qi::_pass = (qi::_1 >=SUM and qi::_1<=AVG) ];
Note the %= to ensure attribute propagation.
Full Working Demonstration
Program code:
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
// #include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
typedef std::string column_name_t;
enum sql_faggregate
{
SUM,
// ....
AVG,
};
typedef std::pair<column_name_t, sql_faggregate> column_and_aggregate;
typedef std::vector<column_and_aggregate> column_and_aggregate_container;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, column_and_aggregate_container(), Skipper>
{
parser() : parser::base_type(aggregates_parser)
{
using namespace qi;
// using phx::bind; using phx::ref; using phx::val;
quoted_string = lexeme [ "'" >> *~qi::char_("'") >> "'" ];
faggr = int_;
agg_pair = quoted_string > ':' > faggr;
aggregates_parser = '{' > agg_pair % ',' > '}';
BOOST_SPIRIT_DEBUG_NODE(aggregates_parser);
}
private:
qi::rule<It, std::string(), qi::space_type> quoted_string;
qi::rule<It, sql_faggregate(), qi::space_type> faggr;
qi::rule<It, column_and_aggregate(), qi::space_type> agg_pair;
qi::rule<It, column_and_aggregate_container(), qi::space_type> aggregates_parser;
};
bool doParse(const std::string& input)
{
typedef std::string::const_iterator It;
auto f(begin(input)), l(end(input));
parser<It, qi::space_type> p;
column_and_aggregate_container data;
try
{
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
for (auto& pair : data)
std::cout << "result: '" << pair.first << "' : " << (int) pair.second << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
} catch(const qi::expectation_failure<It>& e)
{
std::string frag(e.first, e.last);
std::cerr << e.what() << "'" << frag << "'\n";
}
return false;
}
int main()
{
bool ok = doParse("{ 'column 1' : 1, 'column 2' : 0, 'column 3' : 1 }");
return ok? 0 : 255;
}
Print output:
parse success
result: 'column 1' : 1
result: 'column 2' : 0
result: 'column 3' : 1
PS: If you wanted to do the same in semantic actions, you'd probably want to write it like:
agg_pair =
quoted_string [ phx::bind(&column_and_aggregate::first, _val) = _1 ]
> ':'
> faggr [ phx::bind(&column_and_aggregate::second, _val) = _1 ];
You'll see that you can just drop it in the above sample and it works exactly the same. For this particular grammar, it's just more verbose, so I don't recommend it :)
I want to a list of name-value pairs. Each list is terminated by a '.' and EOL. Each name-value pair is separated by a ':'. Each pair is separated by a ';' in the list. E.g.
NAME1: VALUE1; NAME2: VALUE2; NAME3: VALUE3.<EOL>
The problem I have is that the values contain '.' and the last value always consumes the '.' at the EOL. Can I use some sort of lookahead to ensure the last '.' before the EOL is treated differently?
I have created a sample, that presumably looks like what you have. The tweak is in the following line:
value = lexeme [ *(char_ - ';' - ("." >> (eol|eoi))) ];
Note how - ("." >> (eol|eoi))) means: exclude any . that is immediately followed by end-of-line or end-of-input.
Test case (also live on http://liveworkspace.org/code/949b1d711772828606ddc507acf4fb4b):
const std::string input =
"name1: value 1; other name : value #2.\n"
"name.sub1: value.with.periods; other.sub2: \"more fun!\"....\n";
bool ok = doParse(input, qi::blank);
Output:
parse success
data: name1 : value 1 ; other name : value #2 .
data: name.sub1 : value.with.periods ; other.sub2 : "more fun!"... .
Full code:
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <map>
#include <vector>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
typedef std::map<std::string, std::string> map_t;
typedef std::vector<map_t> maps_t;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, maps_t(), Skipper>
{
parser() : parser::base_type(start)
{
using namespace qi;
name = lexeme [ +~char_(':') ];
value = lexeme [ *(char_ - ';' - ('.' >> (eol|eoi))) ];
line = ((name >> ':' >> value) % ';') >> '.';
start = line % eol;
}
private:
qi::rule<It, std::string(), Skipper> name, value;
qi::rule<It, map_t(), Skipper> line;
qi::rule<It, maps_t(), Skipper> start;
};
template <typename C, typename Skipper>
bool doParse(const C& input, const Skipper& skipper)
{
auto f(std::begin(input)), l(std::end(input));
parser<decltype(f), Skipper> p;
maps_t data;
try
{
bool ok = qi::phrase_parse(f,l,p,skipper,data);
if (ok)
{
std::cout << "parse success\n";
for (auto& line : data)
std::cout << "data: " << karma::format_delimited((karma::string << ':' << karma::string) % ';' << '.', ' ', line) << '\n';
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
//if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
} catch(const qi::expectation_failure<decltype(f)>& e)
{
std::string frag(e.first, e.last);
std::cerr << e.what() << "'" << frag << "'\n";
}
return false;
}
int main()
{
const std::string input =
"name1: value 1; other name : value #2.\n"
"name.sub1: value.with.periods; other.sub2: \"more fun!\"....\n";
bool ok = doParse(input, qi::blank);
return ok? 0 : 255;
}