下面的代码成功地将"-5.24 * [HelloWorld : w]"
之类的字符串解析为struct summand
,但我需要将"-5.24 * [HelloWorld : w] -7 * [HelloWorld : b] -8.24 * [HelloWorld : h]"
之类的字符串解析为vector<summand>
。所以我的问题是如何做到这一点?
#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;
}
我试图改变
start %= float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']';
到
start %= (float_ >> -lit('*') >> '[' >> lexeme[alpha >> *alnum] >> ':' >> no_case[rectProperties] >> ']')[&addToVector];
但这会导致编译时错误:
/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'
和
/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'
答案 0 :(得分:2)
你需要在语义动作中使用“懒惰的函子”。凤凰城将这些“演员”命名为。要将常规函数用作actor,只需绑定它:
summand_rule %=
float_
>> -lit('*')
>> '['
>> lexeme[alpha >> *alnum]
>> ':'
>> no_case[rectProperties]
>> ']';
start %= summand_rule [phoenix::bind(&addToVector, qi::_1)];
查看编译示例 live on Coliru ,打印
pushed: (-5.24 HelloWorld 8)
现在,我得到了一个明显的印象,你只是想解析一个求和向量,在这种情况下你应该只使用operator%
(列表解析器)或operator*
(Kleen star)。
以下是使用问题中的三和输入文本并使用
的示例 qi::rule<Iterator, std::vector<summand>(), ascii::space_type> start;
// defined as
start = *summand_rule;
没有其他要求!演示程序 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;
}
打印
pushed: (-5.24 HelloWorld 8)
pushed: (-7 HelloWorld 32)
pushed: (-8.24 HelloWorld 128)
注意:
phoenix
或语义操作client::summands
addToVector
供将来参考:
#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;
}