My code below successfully parse string like "-5.24 * [HelloWorld : w]"
into struct summand
but i need that it would be possible to parse for example string like "-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]"
into a vector<summand>
. So my question is how to do that?
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.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;
namespace phoenix = boost::phoenix;
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;
};
std::vector<summand> summands;
void addToVector(summand const& sum)
{
summands.push_back(sum);
}
}
BOOST_FUSION_ADAPT_STRUCT(client::summand,
(float, factor)
(std::string, nodeName)
(RectProperty, property)
);
namespace client {
template <typename Iterator>
struct summand_parser : qi::grammar<Iterator, summand(), ascii::space_type>
{
summand_parser() : summand_parser::base_type(start)
{
using spirit::float_;
using ascii::no_case;
using ascii::alpha;
using ascii::alnum;
using qi::lexeme;
using qi::lit;
start %= float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
}
qi::rule<Iterator, summand(), ascii::space_type> start;
};
}
float computeSimpleMathExpression(const char * pStr)
{
using boost::spirit::ascii::space;
typedef std::string::const_iterator iterator_type;
typedef client::summand_parser<iterator_type> summand_parser;
summand_parser g; // Our grammar
std::string str("-5.24 * [ HelloWorld : w ]");
client::summand sum;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, g, space, sum);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << boost::fusion::as_vector(sum) << std::endl;
std::cout << str << " Parses OK: " << std::endl;
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
return 0;
}
I have tried to change
start %= float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
to
start %= (float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']')[&addToVector];
but this leads to compile time errors:
/Users/apple/Documents/ios/framework/boost.framework/Headers/spirit/home/support/action_dispatch.hpp:204:15: No viable conversion from 'boost::fusion::vector3<float, boost::fusion::vector2<char, std::__1::vector<char, std::__1::allocator<char> > >, unsigned int>' to 'const client::summand'
and
/Users/apple/Documents/ios/framework/boost.framework/Headers/spirit/home/qi/detail/assign_to.hpp:152:20: No matching conversion for static_cast from 'const boost::fusion::vector3<float, boost::fusion::vector2<char, std::__1::vector<char, std::__1::allocator<char> > >, unsigned int>' to 'client::summand'
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:
phoenix
or semantic actionsclient::summands
addToVector
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;
}