In Boost::Spirit, how can I trigger an expectation_failure from a function bound with Boost::Bind?
Background: I parse a large file that contains complex entries. When an entry is inconsistent with a previous entry I want to fail and throw an expectation_failure (containing proper parse position information). When I parse an entry I bind a function that decides if the entry is inconsistent with something seen before.
I made up a little toy example that shows the point. Here I simply want to throw an expectation_failure when the int is not divisible by 10:
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/bind.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
namespace qi = boost::spirit::qi;
namespace classic = boost::spirit::classic;
void checkNum(int const& i) {
if (i % 10 != 0) // >> How to throw proper expectation_failure? <<
std::cerr << "ERROR: Number check failed" << std::endl;
}
template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, int(), Skipper> {
MyGrammar() : MyGrammar::base_type(start) {
start %= qi::eps > qi::int_[boost::bind(&checkNum, _1)];
}
qi::rule<Iterator, int(), Skipper> start;
};
template<class PosIter>
std::string errorMsg(PosIter const& iter) {
const classic::file_position_base<std::string>& pos = iter.get_position();
std::stringstream msg;
msg << "parse error at file " << pos.file
<< " line " << pos.line << " column " << pos.column << std::endl
<< "'" << iter.get_currentline() << "'" << std::endl
<< std::setw(pos.column) << " " << "^- here";
return msg.str();
}
int main() {
std::string in = "11";
typedef std::string::const_iterator Iter;
typedef classic::position_iterator2<Iter> PosIter;
MyGrammar<PosIter, qi::space_type> grm;
int i;
PosIter it(in.begin(), in.end(), "<string>");
PosIter end;
try {
qi::phrase_parse(it, end, grm, qi::space, i);
if (it != end)
throw std::runtime_error(errorMsg(it));
} catch(const qi::expectation_failure<PosIter>& e) {
throw std::runtime_error(errorMsg(e.first));
}
return 0;
}
Throwing an expectation_failure would mean that I get an error message like this on an int that is not divisible by 10:
parse error at file <string> line 1 column 2
'11'
^- here
You can use the _pass placeholder in phoenix to enforce a parse fail. Something like this should work.
bool myfunc(int i) {return i%10 == 0;}
...
_int [ _pass = phoenix::bind(myfunc,_1)]
Years late but anyway:
If you absolutely want to throw an exception and want the on_error to catch it, you have to throw the expectation_exception from the qi namespace because the error handler on_error does catch nothing else.
This might apply to a semantic action or a custom parser implementation.
Would look like:
boost::throw_exception(Exception(first, last, component.what(context)));
where Exception is a qi::expactation_exception and nothing else.
If you do not have a component at hand like in a semantic action you must provide your own qi::info object instead of component.what(..).
You can throw from everywhere inside a context guarded by on_error.
Related
I was toying with Boost.Spirit X3 calculator example when I encountered an error I couldn't get my head around.
I minimized the program to reduce complexity still throwing the same error.
Say I want to parse an input as a list of statements (strings) followed by a delimiter (';').
This is my structure:
namespace client { namespace ast
{
struct program
{
std::list<std::string> stmts;
};
}}
BOOST_FUSION_ADAPT_STRUCT(client::ast::program,
(std::list<std::string>, stmts)
)
The grammar is as follows:
namespace client
{
namespace grammar
{
x3::rule<class program, ast::program> const program("program");
auto const program_def =
*((*char_) > ';')
;
BOOST_SPIRIT_DEFINE(
program
);
auto calculator = program;
}
using grammar::calculator;
}
Invoked
int
main()
{
std::cout <<"///////////////////////////////////////////\n\n";
std::cout << "Expression parser...\n\n";
std::cout << //////////////////////////////////////////////////\n\n";
std::cout << "Type an expression...or [q or Q] to quit\n\n";
typedef std::string::const_iterator iterator_type;
typedef client::ast::program ast_program;
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
auto& calc = client::calculator; // Our grammar
ast_program program; // Our program (AST)
iterator_type iter = str.begin();
iterator_type end = str.end();
boost::spirit::x3::ascii::space_type space;
bool r = phrase_parse(iter, end, calc, space, program);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout<< '\n';
std::cout << "-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
Error I get is
/opt/boost_1_66_0/boost/spirit/home/x3/support/traits/container_traits.hpp: In instantiation of ‘struct boost::spirit::x3::traits::container_value<client::ast::program, void>’:
.
.
.
/opt/boost_1_66_0/boost/spirit/home/x3/support/traits/container_traits.hpp:76:12: error: no type named ‘value_type’ in ‘struct client::ast::program’
struct container_value
/opt/boost_1_66_0/boost/spirit/home/x3/operator/detail/sequence.hpp:497:72: error: no type named ‘type’ in ‘struct boost::spirit::x3::traits::container_value<client::ast::program, void>’
, typename traits::is_substitute<attribute_type, value_type>::type());
^~~~~~
Things I tried:
Following Getting boost::spirit::qi to use stl containers
Even though it uses Qi I nonetheless tried:
namespace boost{namespace spirit{ namespace traits{
template<>
struct container_value<client::ast::program>
//also with struct container<client::ast::program, void>
{
typedef std::list<std::string> type;
};
}}}
You see I'm kinda in the dark, so expectably to no avail.
parser2.cpp:41:8: error: ‘container_value’ is not a class template
struct container_value<client::ast::program>
^~~~~~~~~~~~~~~
In the same SO question I author says "There is one known limitation though, when you try to use a struct that has a single element that is also a container compilation fails unless you add qi::eps >> ... to your rule."
I did try adding a dummy eps also without success.
Please, help me decipher what that error means.
Yup. This looks like another limitation with automatic propagation of attributes when single-element sequences are involved.
I'd probably bite the bullet and change the rule definition from what it is (and what you'd expect to work) to:
x3::rule<class program_, std::vector<std::string> >
That removes the root of the confusion.
Other notes:
you had char_ which also eats ';' so the grammar would never succeed because no ';' would follow a "statement".
your statements aren't lexeme, so whitespace is discarded (is this what you meant? See Boost spirit skipper issues)
your statement could be empty, which meant parsing would ALWAYS fail at the end of input (where it would always read an empty state, and then discover that the expected ';' was missing). Fix it by requiring at least 1 character before accepting a statement.
With some simplifications/style changes:
Live On Coliru
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <list>
namespace x3 = boost::spirit::x3;
namespace ast {
using statement = std::string;
struct program {
std::list<statement> stmts;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::program, stmts)
namespace grammar {
auto statement
= x3::rule<class statement_, ast::statement> {"statement"}
= +~x3::char_(';');
auto program
= x3::rule<class program_, std::list<ast::statement> > {"program"}
= *(statement >> ';');
}
#include <iostream>
#include <iomanip>
int main() {
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using It = std::string::const_iterator;
for (std::string str; std::getline(std::cin, str);) {
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
auto &parser = grammar::program;
ast::program program; // Our program (AST)
It iter = str.begin(), end = str.end();
if (phrase_parse(iter, end, parser, x3::space, program)) {
std::cout << "Parsing succeeded\n";
for (auto& s : program.stmts) {
std::cout << "Statement: " << std::quoted(s, '\'') << "\n";
}
}
else
std::cout << "Parsing failed\n";
if (iter != end)
std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end), '\'') << "\n";
}
}
Which for input "a;b;c;d;" prints:
Parsing succeeded
Statement: 'a'
Statement: 'b'
Statement: 'c'
Statement: 'd'
in the next simple code I receive the "access violation" exception. Why it happens? I can`t get the reason.
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
typedef boost::spirit::ascii::space_type TSkipper;
struct MyField
{
bool isConst;
std::string mtype;
std::string name;
};
BOOST_FUSION_ADAPT_STRUCT
(
MyField,
(bool, isConst)
(std::string, mtype)
(std::string, name)
)
template<typename Iterator, typename TSkipper = boost::spirit::ascii::space_type>
struct field_grammar : qi::grammar < Iterator, Field(), TSkipper >
{
field_grammar() : field_grammar::base_type(field, "field_grammar")
{
// must parse values such as: int, list, i, j9_
valid_symbols %= lexeme[qi::char_("a-zA-Z") > *(qi::char_("a-zA-Z0-9_"))];
valid_symbols.name("valid_symbols");
field %= qi::matches["const"] >> valid_symbols >> valid_symbols;
field.name("field");
BOOST_SPIRIT_DEBUG_NODES((valid_symbols)(field));
}
boost::spirit::qi::rule<Iterator, std::string(), TSkipper> valid_symbols;
boost::spirit::qi::rule<Iterator, Field(), TSkipper> field;
};
And I use this grammar in the next way:
void SpiritTestSimple()
{
std::string mdata = "int destroyWindow";
std::string::const_iterator first = mdata.begin(), last = mdata.end();
field_grammar<std::string::const_iterator> test_grammar;
Field parsed;
bool is_parsed;
try
{
is_parsed = qi::phrase_parse(first, last, test_grammar,
boost::spirit::ascii::space, parsed);
}
catch (const qi::expectation_failure<std::string::const_iterator>& e)
{
std::string frag(e.first, e.last);
std::cout << e.what() << "'" << frag << "'" << std::endl;
}
BOOST_ASSERT(is_parsed && "the example not parsed");
}
I receive the exception when executed the "qi::phrase_parse" function.
Why it happens? And how to fix it?
I use msvs2013.
I also use VS2013. It works okay, and I can't see much wrong with it except for long shots:
you need to qualify qi::lexeme (with me that lead to a compilation error)
you have Field reference that probably need to be MyField (this too should lead to a compilation error)
technically, your program has undefined behaviour because is_parsed might be uninitialized in the ASSERT macro.
You could try the following version: Live On Coliru
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct MyField
{
bool isConst = false;
std::string mtype = "";
std::string name = "";
friend std::ostream& operator<<(std::ostream& os, MyField const& mf)
{
return os << "MyField["
<< "isConst:" << std::boolalpha << mf.isConst
<< "\nmtype:" << mf.mtype
<< "\nname: " << mf.name << "]";
}
};
BOOST_FUSION_ADAPT_STRUCT
(
MyField,
(bool, isConst)
(std::string, mtype)
(std::string, name)
)
template<typename Iterator, typename TSkipper = boost::spirit::ascii::space_type>
struct field_grammar : qi::grammar <Iterator, MyField(), TSkipper>
{
field_grammar() : field_grammar::base_type(field, "field_grammar")
{
// must parse values such as: int, list, i, j9_
valid_symbols = qi::char_("a-zA-Z") >> *(qi::char_("a-zA-Z0-9_"));
field = qi::matches["const"] >> valid_symbols >> valid_symbols;
BOOST_SPIRIT_DEBUG_NODES((valid_symbols)(field));
}
boost::spirit::qi::rule<Iterator, std::string()> valid_symbols;
boost::spirit::qi::rule<Iterator, MyField(), TSkipper> field;
};
void SpiritTestSimple()
{
std::string const mdata = "int destroyWindow";
std::string::const_iterator first = mdata.begin(), last = mdata.end();
field_grammar<std::string::const_iterator> test_grammar;
try
{
MyField parsed;
if (qi::phrase_parse(first, last, test_grammar,
boost::spirit::ascii::space, parsed))
{
std::cout << "Parsed: " << parsed << "\n";
} else
{
std::cout << "Failed to parse '" << std::string(first, last) << "'\n";
}
}
catch (const qi::expectation_failure<std::string::const_iterator>& e)
{
std::string frag(e.first, e.last);
std::cout << e.what() << "'" << frag << "'" << std::endl;
}
}
int main()
{
SpiritTestSimple();
}
If that doesn't WorksForYou(TM) it's time to look at patch-levels, library versions, architecture, optimization flags and all that jazz.
I have the following code:
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any, boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
start %= boost::spirit::qi::int_;
}
boost::spirit::qi::rule<Iterator, boost::any, boost::spirit::qi::ascii::space_type> start;
};
int main()
{
const std::string input_data("1");
boost::any var;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try
{
std::cout << boost::any_cast<int>(var) << '\n';
}
catch (const boost::bad_any_cast& ex)
{
std::cerr << ex.what() << '\n';
}
}
else
{
std::cout << "Parsing failed \n";
}
}
Output
Parsing succeeded
boost::bad_any_cast: failed conversion using boost::any_cast
I think that cast should work fine in such case. Am i wrong? How can I fix it?
Initialize your any!
boost::any var = 0;
Spirit binds the attribute value by reference, so it will cast to int& before invoking the qi::int_ parser.
On a side note, using boost::any with Spirit seems like misguided plan. I can't see why you'd diverge from statically known types and e.g. Variant. But I'll leave that up to you :)
P.S. Oh, and you forgot some more parentheses like here :). See it Live On Coliru now
template <typename Iterator>
struct parse_grammar
: qi::grammar<Iterator, std::string()>
{
parse_grammar()
: parse_grammar::base_type(start_p, "start_p"){
a_p = ',' > qi::double_;
b_p = *a_p;
start_p = qi::double_ > b_p >> qi::eoi;
}
qi::rule<Iterator, std::string()> a_p;
qi::rule<Iterator, std::string()> b_p;
qi::rule<Iterator, std::string()> start_p;
};
// implementation
std::vector<double> parse(std::istream& input, const std::string& filename)
{
// iterate over stream input
typedef std::istreambuf_iterator<char> base_iterator_type;
base_iterator_type in_begin(input);
// convert input iterator to forward iterator, usable by spirit parser
typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
forward_iterator_type fwd_end;
// prepare output
std::vector<double> output;
// wrap forward iterator with position iterator, to record the position
typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
pos_iterator_type position_begin(fwd_begin, fwd_end, filename);
pos_iterator_type position_end;
parse_grammar<pos_iterator_type> gram;
// parse
try
{
qi::phrase_parse(
position_begin, position_end, // iterators over input
gram, // recognize list of doubles
ascii::space); // comment skipper
}
catch(const qi::expectation_failure<pos_iterator_type>& e)
{
const classic::file_position_base<std::string>& pos = e.first.get_position();
std::stringstream msg;
msg <<
"parse error at file " << pos.file <<
" line " << pos.line << " column " << pos.column << std::endl <<
"'" << e.first.get_currentline() << "'" << std::endl <<
" " << "^- here";
throw std::runtime_error(msg.str());
}
// return result
return output;
}
I have this above sample code(Code used from boost-spirit website for example here).
In the grammar in the rule a_p I want to use semantic action and call a method and pass the iterator to it something as below:
a_p = ',' > qi::double_[boost::bind(&parse_grammar::doStuff(), this,
boost::ref(position_begin), boost::ref(position_end)];
and if the signature of the method doStuff is like this:
void doStuff(pos_iterator_type const& first, pos_iterator_type const& last);
Any ideas how to do this?
I do not mind any way(if I can do it using boost::phoenix or something not sure how) as long as to the method the iterators are passed with their current state.
I'm not completely sure why you think you 'need' what you describe. I'm afraid the solution to your actual task might be very simple:
start_p = qi::double_ % ',' > qi::eoi;
However, since the actual question is quite interesting, and the use of position interators in combination with istream_buf (rather than just the usual (slower) boost::spirit::istream_iterator) has it's merit, I'll show you how to do it with the semantic action as well.
For a simple (but rather complete) test main of
int main()
{
std::istringstream iss(
"1, -3.4 ,3.1415926\n"
",+inF,-NaN ,\n"
"2,-.4,4.14e7\n");
data_t parsed = parse(iss, "<inline-test>");
std::cout << "Done, parsed " << parsed.size() << " values ("
<< "min: " << *std::min_element(parsed.begin(), parsed.end()) << ", "
<< "max: " << *std::max_element(parsed.begin(), parsed.end()) << ")\n";
}
The output with the semantic action now becomes:
debug ('start_p') at <inline-test>:1:[1..2] '1' = 1
debug ('start_p') at <inline-test>:1:[4..8] '-3.4' = -3.4
debug ('start_p') at <inline-test>:1:[10..19] '3.1415926' = 3.14159
debug ('start_p') at <inline-test>:2:[2..6] '+inF' = inf
debug ('start_p') at <inline-test>:2:[7..11] '-NaN' = -nan
debug ('start_p') at <inline-test>:3:[1..2] '2' = 2
debug ('start_p') at <inline-test>:3:[3..6] '-.4' = -0.4
debug ('start_p') at <inline-test>:3:[7..13] '4.14e7' = 4.14e+07
Done, parsed 8 values (min: -3.4, max: inf)
See it live at http://liveworkspace.org/code/8a874ef3...
Note how it
demonstrates access to the name of the actual parser instance ('start_p')
demonstrates accces to the full source iterator range
shows how to do specialized processing inside the semantic action
I still suggest using qi::double_ to parse the raw input, because it is the only thing I know that easily handles all cases (see test data and this other question: Is it possible to read infinity or NaN values using input streams?)
demonstrates parsing the actual data into the vector efficiently by displaying statistics of the parsed values
Full Code
Here is the full code for future reference:
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_multi_pass.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/phoenix/function/adapt_function.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace classic = boost::spirit::classic;
namespace ascii = boost::spirit::ascii;
typedef std::vector<double> data_t;
///////// USING A FREE FUNCTION
//
template <typename Grammar, typename Range>
double doStuff_(Grammar &grammar, Range pos_range)
{
// for efficiency, cache adhoc grammar:
static const qi::rule <typename Range::iterator, double()> r_double = qi::double_;
static const qi::grammar<typename Range::iterator, double()> g_double(r_double); // caching just the rule may be enough, actually
double value = 0;
qi::parse(pos_range.begin(), pos_range.end(), g_double, value);
std::cout << "debug ('" << grammar.name() << "') at "
<< pos_range.begin().get_position().file << ":"
<< pos_range.begin().get_position().line << ":["
<< pos_range.begin().get_position().column << ".."
<< pos_range.end ().get_position().column << "]\t"
<< "'" << std::string(pos_range.begin(),pos_range.end()) << "'\t = "
<< value
<< '\n';
return value;
}
BOOST_PHOENIX_ADAPT_FUNCTION(double, doStuff, doStuff_, 2)
template <typename Iterator, typename Skipper>
struct parse_grammar : qi::grammar<Iterator, data_t(), Skipper>
{
parse_grammar()
: parse_grammar::base_type(start_p, "start_p")
{
using qi::raw;
using qi::double_;
using qi::_1;
using qi::_val;
using qi::eoi;
using phx::push_back;
value_p = raw [ double_ ] [ _val = doStuff(phx::ref(*this), _1) ];
start_p = value_p % ',' > eoi;
// // To use without the semantic action (more efficient):
// start_p = double_ % ',' >> eoi;
}
qi::rule<Iterator, data_t::value_type(), Skipper> value_p;
qi::rule<Iterator, data_t(), Skipper> start_p;
};
// implementation
data_t parse(std::istream& input, const std::string& filename)
{
// iterate over stream input
typedef std::istreambuf_iterator<char> base_iterator_type;
base_iterator_type in_begin(input);
// convert input iterator to forward iterator, usable by spirit parser
typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
forward_iterator_type fwd_end;
// wrap forward iterator with position iterator, to record the position
typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
pos_iterator_type position_begin(fwd_begin, fwd_end, filename);
pos_iterator_type position_end;
parse_grammar<pos_iterator_type, ascii::space_type> gram;
data_t output;
// parse
try
{
if (!qi::phrase_parse(
position_begin, position_end, // iterators over input
gram, // recognize list of doubles
ascii::space, // comment skipper
output) // <-- attribute reference
)
{
std::cerr << "Parse failed at "
<< position_begin.get_position().file << ":"
<< position_begin.get_position().line << ":"
<< position_begin.get_position().column << "\n";
}
}
catch(const qi::expectation_failure<pos_iterator_type>& e)
{
const classic::file_position_base<std::string>& pos = e.first.get_position();
std::stringstream msg;
msg << "parse error at file " << pos.file
<< " line " << pos.line
<< " column " << pos.column
<< "\n\t'" << e.first.get_currentline()
<< "'\n\t " << std::string(pos.column, ' ') << "^-- here";
throw std::runtime_error(msg.str());
}
return output;
}
int main()
{
std::istringstream iss(
"1, -3.4 ,3.1415926\n"
",+inF,-NaN ,\n"
"2,-.4,4.14e7\n");
data_t parsed = parse(iss, "<inline-test>");
std::cout << "Done, parsed " << parsed.size() << " values ("
<< "min: " << *std::min_element(parsed.begin(), parsed.end()) << ", "
<< "max: " << *std::max_element(parsed.begin(), parsed.end()) << ")\n";
}
I'm a newbie in boost::spirit. I wrote the program to parse a SQL statement, like "select * from table where conditions". It compile failed. A large of template errors reported. So, would somebody help me?
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct db_select {
void exec() {}
std::string filed;
std::string table;
std::string condition;
};
std::ostream& operator<<(std::ostream& os, const db_select& se) {
return os << "filed: " << se.filed << " table: " << se.table << " condition: " << se.condition;
}
template <class Iterator>
struct selecter : qi::grammar<Iterator, db_select (), ascii::space_type> {
selecter() : selecter::base_type(se) {
se %= "select" >> +qi::char_ << "from" << +qi::char_ << "where" << +qi::char_;
}
qi::rule<Iterator, db_select (), ascii::space_type> se;
};
int main(int argc, char* argv[]) {
if (argc < 2)
return -1;
std::string str(argv[1]);
const char* first = str.c_str();
const char* last = &str[str.size()];
selecter<const char*> se;
db_select rst;
bool r = qi::phrase_parse(first, last, se, ascii::space, rst);
if (!r || first != last) {
std::cout << "parse failed, at: " << std::string(first, last) << std::endl;
return -1;
} else
std::cout << "success, " << rst << std::endl;
return 0;
}
Edit Finally behind a computer, revised answer:
There were three things to note
1. Syntax errors
The parser expression contained errors (<< instead of >> as intended). This accounts for a lot of compile errors. Note the appearance of ******* in the compiler error:
/.../qi/nonterminal/rule.hpp|176 col 13| error: no matching function for call to ‘assertion_failed(mpl_::failed************
which is designed to lead you to the corresponding comment in the source code:
// Report invalid expression error as early as possible.
// If you got an error_invalid_expression error message here,
// then the expression (expr) is not a valid spirit qi expression.
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
Easily fixed. One down, two to go!
2. Adapting the struct
In order to assign to your datatype as an rule attribute, you need to make it fusion compatible. The most convenient way:
BOOST_FUSION_ADAPT_STRUCT(db_select,
(std::string,field)(std::string,table)(std::string,condition));
Now the code compiles. But the parsing fails. One more to go:
3. Lexemes
Further more you will need to take measures to avoid 'eating' your query keywords with the +qi::char_ expressions.
As a basis, consider writing it something like
lexeme [ (!lit("where") >> +qi::graph) % +qi::space ]
lexeme inhibits the skipper for the enclosed expression
! Asserts that the specified keyword must not match
Lastly, look at the docs for qi::no_case to do case-insensitive matching.
FULL CODE
#include <iostream>
#include <string>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct db_select {
void exec() {}
std::string field;
std::string table;
std::string condition;
};
BOOST_FUSION_ADAPT_STRUCT(db_select,(std::string,field)(std::string,table)(std::string,condition));
std::ostream& operator<<(std::ostream& os, const db_select& se) {
return os << "field: " << se.field << " table: " << se.table << " condition: " << se.condition;
}
template <class Iterator>
struct selecter : qi::grammar<Iterator, db_select (), qi::space_type> {
selecter() : selecter::base_type(se) {
using namespace qi;
se %= "select"
>> lexeme [ (!lit("from") >> +graph) % +space ] >> "from"
>> lexeme [ (!lit("where") >> +graph) % +space ] >> "where"
>> +qi::char_;
}
qi::rule<Iterator, db_select (), qi::space_type> se;
};
int main(int argc, char* argv[]) {
if (argc < 2)
return -1;
std::string str(argv[1]);
const char* first = str.c_str();
const char* last = &str[str.size()];
selecter<const char*> se;
db_select rst;
bool r = qi::phrase_parse(first, last, se, qi::space, rst);
if (!r || first != last) {
std::cout << "parse failed, at: " << std::string(first, last) << std::endl;
return -1;
} else
std::cout << "success, " << rst << std::endl;
return 0;
}
Test:
g++ test.cpp -o test
./test "select aap, noot, mies from table where field = 'value'"
Output:
success, field: aap,noot,mies table: table condition: field='value'