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

时间:2017-03-17 13:25:29

标签: c++ parsing boost-spirit boost-spirit-qi

(问题从Spirit-general mailing list解除)

您好,

我在精神qi的解析器中工作。语法运行良好,但是我有一些问题用Semantic Actions填充我的struct实例。

使用直接结构属性,例如" Request.id"和#34; 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;
}

谢谢! 埃米利亚诺

1 个答案:

答案 0 :(得分:5)

与往常一样,我将建议解决方案不使用语义操作(Boost Spirit: "Semantic actions are evil"?)。我真的相信语义行为应该谨慎使用,因为它们大多有可能使事情复杂化。 (例如newdelete,您应该使用它们的时间非常有限。)

在这种情况下,你几乎就在那里:自动属性传播合成a tuple of permutation members in sequence

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

BOOST_FUSION_ADAPT_STRUCT(Request::Info, id, desc)
BOOST_FUSION_ADAPT_STRUCT(Request, info, id, url, list)

语法本身存在一些问题((a ^ b) % ','(a | b) % ','不相似:它会解析abab,{{ 1}},ba等。)。

我将规则拆分了一点以删除冗余并使用“智能”分隔符来指望在正确的位置使用逗号:

a,ba,b

完整的语法变为:

delim_  = &lit('}') | ','; // unless at end of block, expect a comma

注意

  • 它如何不使用任何语义动作。
  • 我使用继承的属性来重用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_) >> '}'; key_。您可以使用Nabialek技巧进一步推广非字符串属性
prop_
  • 我选择将headper隐藏在外部接口中(skipper是语法规范的一部分,所以调用者可能不应该指定它。)

演示

<强> Live On Coliru

    start    = skip(space) [ request_ ];

打印:

#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";
}