I am trying to parse an expression which can also contain identifiers and push each element into an std::vector <std::string>
, and I have come up with the following grammar:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <vector>
namespace qi = boost::spirit::qi;
struct Tokeniser
: boost::spirit::qi::grammar <std::string::const_iterator, std::vector <std::string> (), boost::spirit::ascii::space_type>
{
Tokeniser() : Tokeniser::base_type(expression)
{
namespace qi = boost::spirit::qi;
expression =
term >>
*( (qi::string("+")[qi::_val.push_back(qi::_1)] >> term) |
(qi::string("-")[qi::_val.push_back(qi::_1)] >> term) );
term =
factor >>
*( (qi::string("*")[qi::_val.push_back(qi::_1)] >> factor) |
(qi::string("/")[qi::_val.push_back(qi::_1)] >> factor) );
factor =
(identifier | myDouble_)[qi::_val.push_back(qi::_1)] |
qi::string("(")[qi::_val.push_back(qi::_1)] >> expression >> qi::string(")")[qi::_val.push_back(qi::_1)];
identifier = qi::raw [ qi::lexeme[ (qi::alpha | '_') >> *(qi::alnum | '_') ] ];
myDouble_ = qi::raw [ qi::double_ ];
}
boost::spirit::qi::rule<std::string::const_iterator, std::vector <std::string> (), boost::spirit::ascii::space_type> expression;
boost::spirit::qi::rule<std::string::const_iterator, boost::spirit::ascii::space_type> factor;
boost::spirit::qi::rule<std::string::const_iterator, boost::spirit::ascii::space_type> term;
boost::spirit::qi::rule<std::string::const_iterator, std::string(), boost::spirit::ascii::space_type> identifier;
boost::spirit::qi::rule<std::string::const_iterator, std::string(), boost::spirit::ascii::space_type> myDouble_;
};
However, I get the following error 'const struct boost::phoenix::actor<boost::spirit::attribute<0> >' has no member named 'push_back'
.
Is there a direct way to perform what I am trying to do?
Yeah, the placeholder types do (obviously) not have
push_back
member.C++ is strong typed. Any deferred action is an "illusion": actors are represented in expression templates by composing special-purpose types that can be "evaluated" later.
Intro To Expression Templates
Live On Coliru
Just in case you want to wrap your head around how it actually works, a simple example from scratch. The comments describe what the various parts of the code do:
Output is exactly what you'd expect:
Fixing The Action:
You'll have to "describe" the
push_back
operation instead of trying to find such an operation on the placeholder. Phoenix has your back:Now I'd simplify the actions to use
phoenix::push_back
:However, this has the additional problem that
_val
gets resolved to the attribute type of the rule. But some of your rules do not declare an attribute type, so it defaults toqi::unused_type
. Clearly, generating the "push_back" evaluation code for that attribute is not going to work forunused_type
.Let's fix those declarations:
Other Problems!
When we do this, the tokens remain largely empty. What gives?
In the presence of semantic actions, automatic attribute propagation is inhibited. So, you have to work to get the contents of sub expressions appended to the final token vector.
Again, using Phoenix's STL support:
Now, a test with Live On Coliru
Prints
The Big Cleanup
In general, avoid semantic actions (see my answer Boost Spirit: "Semantic actions are evil"? - notably the bullet about side-effects). Most of the time, you can get away with automatic attribute propagation. I'd say this is the key selling point of Boost Spirit.
Further simplifying things around skipping/lexemes (Boost spirit skipper issues) does reduce code and compilation times significantly:
Live On Coliru
Still prints
Remaining problems
Have you thought about backtracking behaviour? I think you need some judiciously placed
qi::hold[]
directives inside your rules, see e.g. Understanding Boost.spirit's string parserBIG QUESTIONS: