You need to have "lazy functors" in semantic actions. Phoenix names these "Actors". To use a regular function as an actor, just bind it:
summand_rule %=
float_
>> -lit('*')
>> '['
>> lexeme[alpha >> *alnum]
>> ':'
>> no_case[rectProperties]
>> ']';
start %= summand_rule [phoenix::bind(&addToVector, qi::_1)];
See a compiling example live on Coliru, printing
pushed: (-5.24 HelloWorld 8)
Now, I get the distinct impression you're simply trying to parse a vector of summands, in which case you should just use operator%
(the list parser) or operator*
(Kleen star).
Here's the example using the three-summand input text from the question and using
qi::rule<Iterator, std::vector<summand>(), ascii::space_type> start;
// defined as
start = *summand_rule;
Nothing else required! The demo program Live on Coliru
int main()
{
std::vector<client::summand> parsed;
parseSummandsInto("-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]", parsed);
for(auto const& summand : parsed)
std::cout << "pushed: " << boost::fusion::as_vector(summand) << std::endl;
}
Prints
pushed: (-5.24 HelloWorld 8)
pushed: (-7 HelloWorld 32)
pushed: (-8.24 HelloWorld 128)
Note:
- removed the need for
phoenix
or semantic actions - removed the need for a global variable
client::summands
- removed the need for
addToVector
- in total 33 lines of code less (89 vs. 122)
Full code
For future reference:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <string>
#include <vector>
enum RectProperty {
RectPropertyNone = 0,
RectPropertyLeft = 1<<0,
RectPropertyRight = 1<<1,
RectPropertyCentreX = 1<<2,
RectPropertyWidth = 1<<3,
RectPropertyTop = 1<<4,
RectPropertyBottom = 1<<5,
RectPropertyCentreY = 1<<6,
RectPropertyHeight = 1<<7
};
namespace client
{
namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
struct rectProperties_ : qi::symbols<char, unsigned>
{
rectProperties_() {
add ("cx" , RectPropertyCentreX)
("cy" , RectPropertyCentreY)
("w" , RectPropertyWidth)
("h" , RectPropertyHeight)
("l" , RectPropertyLeft)
("r" , RectPropertyRight)
("t" , RectPropertyTop)
("b" , RectPropertyBottom)
;
}
} rectProperties;
struct summand {
float factor;
std::string nodeName;
RectProperty property;
};
}
BOOST_FUSION_ADAPT_STRUCT(client::summand,
(float, factor)
(std::string, nodeName)
(RectProperty, property)
)
namespace client {
template <typename Iterator>
struct summand_parser : qi::grammar<Iterator, std::vector<summand>(), ascii::space_type>
{
summand_parser() : summand_parser::base_type(start)
{
using namespace ascii;
summand_rule %= qi::float_ >> -qi::lit('*') >> '[' >> qi::lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
start = *summand_rule;
}
qi::rule<Iterator, summand(), ascii::space_type> summand_rule;
qi::rule<Iterator, std::vector<summand>(), ascii::space_type> start;
};
}
void parseSummandsInto(std::string const& str, std::vector<client::summand>& summands)
{
typedef std::string::const_iterator It;
static const client::summand_parser<It> g;
It iter = str.begin(),
end = str.end();
bool r = phrase_parse(iter, end, g, boost::spirit::ascii::space, summands);
if (r && iter == end)
return;
else
throw "Parse failed";
}
int main()
{
std::vector<client::summand> parsed;
parseSummandsInto("-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]", parsed);
for(auto const& summand : parsed)
std::cout << "pushed: " << boost::fusion::as_vector(summand) << std::endl;
}