seh*_*ehe 2 c++ parsing boost-spirit boost-spirit-qi
(从Spirit-general邮件列表中解除的问题)
你好,
我正在使用精神气的解析器.语法运行良好,但是我有一些问题用Semantic Actions填充我的struct实例.
使用直接结构属性,如"Request.id"和"Request.url",代码正常工作.但我不知道如何填充嵌套结构"Info"中的属性,也不知道如何在"Request.list"中推送值.
这是我的代码(要解析的字符串可以包含任何顺序的值):
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <vector>
#include <string>
struct Request
{
std::string id;
std::string url;
std::vector<std::string> list;
struct Info
{
std::string id;
std::string desc;
};
Info info;
};
BOOST_FUSION_ADAPT_STRUCT(
Request,
(std::string, id)
)
template <typename Iterator>
struct request_parser : boost::spirit::qi::grammar<Iterator, Request(), boost::spirit::ascii::space_type>
{
request_parser() : request_parser::base_type(start)
{
using namespace boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phx = boost::phoenix;
quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];
start %=
BR_OP
>>((DQUOTE >> lit(INFO) >> DQUOTE >> COLON
>> BR_OP
>> ((DQUOTE >> lit(DESC) >> DQUOTE >> COLON >> quoted_string)
^ (DQUOTE >> lit(ID) >> DQUOTE >> COLON >> quoted_string)) % COMMA
>> BR_CL)
^(DQUOTE >> lit(ID) >> DQUOTE >> COLON >> quoted_string[phx::bind(&Request::id, _val) = _1])
^(DQUOTE >> lit(URL) >> DQUOTE >> COLON >> quoted_string[phx::bind(&Request::url, _val) = _1])
^(DQUOTE >> lit(LIST) >> DQUOTE >> COLON
>> SQ_OP
>> quoted_string % COMMA
>> SQ_CL)) % COMMA
>>
BR_CL
;
}
boost::spirit::qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> quoted_string;
boost::spirit::qi::rule<Iterator, Request(), boost::spirit::ascii::space_type> start;
char BR_OP = '{';
char BR_CL = '}';
char DQUOTE = '"';
char COLON = ':';
char SQ_OP = '[';
char SQ_CL = ']';
char COMMA = ',';
const char* LIST = "list";
const char* ID = "id";
const char* URL = "url";
const char* INFO = "info";
const char* DESC = "desc";
};
int main()
{
typedef std::string::iterator iterator_type;
typedef request_parser<iterator_type> requester_parser;
std::string str = "{\"list\":[\"data1\",\"data2\"],\"info\":{\"desc\":\"description\",\"id\":\"23\"},\"id\":\"1234\",\"url\":\"ok.com\"}";
Request rqst;
requester_parser parser;
using boost::spirit::ascii::space;
boost::spirit::qi::phrase_parse(str.begin(), str.end(), parser, space, rqst);
using std::cout;
using std::endl;
cout << rqst.id << endl;
cout << rqst.url << endl;
cout << rqst.list.size() << endl;
cout << rqst.info.id << endl;
cout << rqst.info.desc << endl;
}
Run Code Online (Sandbox Code Playgroud)
谢谢!埃米利亚诺
一如既往,我将建议不使用语义行为的解决方案(提升精神:"语义行为是邪恶的"?).我真的相信语义行为应该谨慎使用,因为它们大多有可能使事情复杂化.(喜欢new
和delete
,你应该使用它们的场合非常有限).
在这种情况下,你几乎就在那里:自动属性传播按顺序合成一组排列成员.
所以,如果你只是按照你的语法顺序调整你的结构,你会没事的:
BOOST_FUSION_ADAPT_STRUCT(Request::Info, id, desc)
BOOST_FUSION_ADAPT_STRUCT(Request, info, id, url, list)
Run Code Online (Sandbox Code Playgroud)
语法本身有一些问题((a ^ b) % ','
没有做类似的东西(a | b) % ','
:它会分析a
,b
,ab
,ba
,a,ba,b
等).
我将规则拆分了一些以消除冗余,并使用"智能"分隔符来指望在正确的位置使用逗号:
delim_ = &lit('}') | ','; // unless at end of block, expect a comma
Run Code Online (Sandbox Code Playgroud)
完整的语法变为:
string_ = '"' >> *~char_('"') >> '"';
key_ = '"' >> string(_r1) >> '"';
prop_ = key_(_r1) >> ':' >> string_ >> delim_;
info_ = key_("info"s) >> ':'
>> '{'
>> (prop_("desc"s) ^ prop_("id"s))
>> '}'
>> delim_
;
list_ = key_("list"s) >> ':'
>> '[' >> string_ % ',' >> ']'
>> delim_
;
request_ = '{' >> (info_ ^ prop_("id"s) ^ prop_("url"s) ^ list_) >> '}';
Run Code Online (Sandbox Code Playgroud)
注意
key_
和prop_
.您可以使用Nabialek技巧进一步推广非字符串属性 start = skip(space) [ request_ ];
Run Code Online (Sandbox Code Playgroud)
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <vector>
#include <string>
using namespace std::literals::string_literals;
struct Request
{
std::string id;
std::string url;
std::vector<std::string> list;
struct Info {
std::string id;
std::string desc;
};
Info info;
};
BOOST_FUSION_ADAPT_STRUCT(Request::Info, id, desc)
BOOST_FUSION_ADAPT_STRUCT(Request, info, id, url, list)
template <typename Iterator>
struct request_parser : boost::spirit::qi::grammar<Iterator, Request()>
{
request_parser() : request_parser::base_type(start)
{
using namespace boost::spirit::qi;
delim_ = &lit('}') | ',';
string_ = '"' >> *~char_('"') >> '"';
key_ = '"' >> string(_r1) >> '"';
prop_ = key_(_r1) >> ':' >> string_ >> delim_;
info_ = key_("info"s) >> ':'
>> '{'
>> (prop_("desc"s) ^ prop_("id"s))
>> '}'
>> delim_
;
list_ = key_("list"s) >> ':'
>> '[' >> string_ % ',' >> ']'
>> delim_
;
request_ = '{' >> (info_ ^ prop_("id"s) ^ prop_("url"s) ^ list_) >> '}';
start = skip(space) [ request_ ];
}
private:
using Skipper = boost::spirit::qi::space_type;
boost::spirit::qi::rule<Iterator, Request()> start;
boost::spirit::qi::rule<Iterator, Request(), Skipper> request_;
boost::spirit::qi::rule<Iterator, Request::Info(), Skipper> info_;
boost::spirit::qi::rule<Iterator, std::vector<std::string>(), Skipper> list_;
boost::spirit::qi::rule<Iterator, std::string(std::string), Skipper> prop_;
// lexemes
boost::spirit::qi::rule<Iterator, std::string()> string_;
// literals
boost::spirit::qi::rule<Iterator, void(std::string)> key_;
boost::spirit::qi::rule<Iterator> delim_;
};
int main()
{
typedef std::string::iterator iterator_type;
typedef request_parser<iterator_type> requester_parser;
std::string str = R"({
"list": ["data1", "data2"],
"info": {
"desc": "description",
"id": "23"
},
"id": "1234",
"url": "ok.com"
})";
Request parsed;
requester_parser parser;
parse(str.begin(), str.end(), parser, parsed);
std::cout << "parsed.id: " << parsed.id << "\n";
std::cout << "parsed.url: " << parsed.url << "\n";
std::cout << "parsed.list.size(): " << parsed.list.size() << "\n";
std::cout << "parsed.info.id: " << parsed.info.id << "\n";
std::cout << "parsed.info.desc: " << parsed.info.desc << "\n";
}
Run Code Online (Sandbox Code Playgroud)
打印:
parsed.id: 1234
parsed.url: ok.com
parsed.list.size(): 2
parsed.info.id: description
parsed.info.desc: 23
Run Code Online (Sandbox Code Playgroud)