使用语义操作填充嵌套结构

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)

谢谢!埃米利亚诺

seh*_*ehe 5

一如既往,我将建议不使用语义行为的解决方案(提升精神:"语义行为是邪恶的"?).我真的相信语义行为应该谨慎使用,因为它们大多有可能使事情复杂化.(喜欢newdelete,你应该使用它们的场合非常有限).

在这种情况下,你几乎就在那里:自动属性传播按顺序合成一组排列成员.

所以,如果你只是按照你的语法顺序调整你的结构,你会没事的:

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)
  • 我选择从外部接口隐藏队长(队长是语法规范的一部分,所以它可能不应该被调用者指定).

演示

Live On Coliru

#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)