I want to parse special constructs and throw the rest away. But I don't want to use a skipper.
I want to get a vector of these constructs, so I use a Kleene Star parser as main rule. But, everytime something gets thrown away, a default constructed element is inserted into the vector.
Here is a made up example. It just looks for the string Test
and throws the rest away, at least this is the plan. But every time the rule garbage
succeeds it adds a default constructed item to the vector in the rule all
, giving an output of 7 insteat of 1. How can I tell Spirit to just add to the vector if the rule item
succeeds?
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace qi = boost::spirit::qi;
struct container {
std::string name;
bool dummy;
};
BOOST_FUSION_ADAPT_STRUCT(::container,
(std::string, name)
(bool, dummy))
int main() {
typedef std::string::const_iterator iterator;
qi::rule<iterator, std::vector<container>()> all;
qi::rule<iterator, container()> item;
qi::rule<iterator, std::string()> string_rule;
qi::rule<iterator> garbage;
all = *(garbage | item);
garbage = qi::char_ - qi::lit("Test");
string_rule = qi::string("Test");
item = string_rule >> qi::attr(true);
std::vector<container> ast;
std::string input = "blaTestbla";
iterator first = input.begin();
iterator last = input.end();
bool result = qi::parse(first, last, all, ast);
if (result) {
result = first == last;
}
if (result) {
std::cout << "Parsed " << ast.size() << " element(s)" << std::endl;
} else {
std::cout << "failure" << std::endl;
}
}
Since sehe's answer was more or less for educational purposes, we have now several solutions:
*garbage >> -(item % *garbage) >> *garbage
*garbage >> *(item >> *garbage)
all = *(garbage | item[phx::push_back(qi::_val,qi::_1)]);
And the solution from cv_and_he:
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace qi = boost::spirit::qi;
struct container {
std::string name;
bool dummy;
};
BOOST_FUSION_ADAPT_STRUCT(::container,
(std::string, name)
(bool, dummy))
struct container_vector { //ADDED
std::vector<container> data;
};
namespace boost{ namespace spirit{ namespace traits //ADDED
{
template <>
struct is_container<container_vector> : boost::mpl::true_ {};
template <>
struct container_value<container_vector> {
typedef optional<container> type;
};
template <>
struct push_back_container<container_vector,optional<container> > {
static bool call(container_vector& cont, const optional<container>& val) {
if(val)
cont.data.push_back(*val);
return true;
}
};
}}}
int main() {
typedef std::string::const_iterator iterator;
qi::rule<iterator, container_vector()> all; //CHANGED
qi::rule<iterator, container()> item;
qi::rule<iterator, std::string()> string_rule;
qi::rule<iterator> garbage;
all = *(garbage | item);
garbage = qi::char_ - qi::lit("Test");
string_rule = qi::string("Test");
item = string_rule >> qi::attr(true);
container_vector ast; //CHANGED
std::string input = "blaTestbla";
iterator first = input.begin();
iterator last = input.end();
bool result = qi::parse(first, last, all, ast);
if (result) {
result = first == last;
}
if (result) {
std::cout << "Parsed " << ast.data.size() << " element(s)" << std::endl; //CHANGED
} else {
std::cout << "failure" << std::endl;
}
}
Although I didn't want to use a skipper I ended up with:
start = qi::skip(garbage.alias())[*item];
This last solution was the fastest (by 1-2%) in my unscientific tests using the c-files of the Linux kernel with my production rules.
A quick fix (not necessarily most performant) would be
all = -(item - garbage) % +garbage;
It prints:
Parsed 3 element(s)
See it Live on Coliru