Boost.Spirit:将字符串对从Qi移植到X3

时间:2017-08-29 21:17:40

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

我有以下工作Qi代码:

struct query_grammar
    : public boost::spirit::qi::grammar<Iterator, string_map<std::string>()>
{
  query_grammar() : query_grammar::base_type(query)
  {
    query = pair >> *(boost::spirit::qi::lit('&') >> pair);
    pair = +qchar >> -(boost::spirit::qi::lit('=') >> +qchar);
    qchar = ~boost::spirit::qi::char_("&=");
  }

  boost::spirit::qi::rule<Iterator, std::map<std::string,std::string>()> query;
  boost::spirit::qi::rule<Iterator, std::map<std::string,std::string>::value_type()> pair;
  boost::spirit::qi::rule<Iterator, char()> qchar;
};

我尝试将其移植到x3:

namespace x3 = boost::spirit::x3;
const x3::rule<class query_char_, char> query_char_ = "query_char";
const x3::rule<class string_pair_, std::map<std::string,std::string>::value_type> string_pair_ = "string_pair";
const x3::rule<class string_map_, std::map<std::string,std::string>> string_map_ = "string_map";

const auto query_char__def = ~boost::spirit::x3::char_("&=");
const auto string_pair__def = +query_char_ >> -(boost::spirit::x3::lit('=') >> +query_char_);
const auto string_map__def = string_pair_ >> *(boost::spirit::x3::lit('&') >> string_pair_);

BOOST_SPIRIT_DEFINE(string_map_)
BOOST_SPIRIT_DEFINE(string_pair_)
BOOST_SPIRIT_DEFINE(query_char_)

但是在尝试使用string_map_解析字符串时出现以下错误:

/usr/include/boost/spirit/home/x3/support/traits/move_to.hpp:209: erreur : no matching function for call to move_to(const char*&, const char*&, std::pair<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >&, boost::mpl::identity<boost::spirit::x3::traits::plain_attribute>::type) 
     detail::move_to(first, last, dest, typename attribute_category<Dest>::type());
     ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我看到了这个答案:Parsing pair of strings fails. Bad spirit x3 grammar并尝试使我的string_pair原始但无济于事。

编辑:

精神示例中的这个示例代码也没有编译,所以我猜问题有点深:

#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;
int main()
{
  std::string input( "cosmic  pizza  " );
  auto iter = input.begin();
  auto end_iter = input.end();
  std::pair<std::string, std::string> result;
  x3::parse( iter, end_iter, *(~x3::char_(' ')) >> ' ' >> *x3::char_, result);
}

1 个答案:

答案 0 :(得分:2)

Qi修复

首先,我必须使用Qi变量修复规则声明才能起作用:

qi::rule<Iterator, std::pair<std::string,std::string>()> pair;

由于value_type具有永远不可分配的pair<key_type const, mapped_type>的简单原因。

这是一个Qi SSCCE:

<强> Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <map>

namespace qi = boost::spirit::qi;

template <typename T> using string_map = std::map<T, T>;

template <typename Iterator>
struct query_grammar : public qi::grammar<Iterator, string_map<std::string>()>
{
    query_grammar() : query_grammar::base_type(query)
    {
        qchar = ~qi::char_("&=");
        pair  = +qchar >> -(qi::lit('=') >> +qchar);
        query = pair >> *(qi::lit('&') >> pair);
    }

  private:
    qi::rule<Iterator, std::map<std::string,std::string>()> query;
    qi::rule<Iterator, std::pair<std::string,std::string>()> pair;
    qi::rule<Iterator, char()> qchar;
};

int main() {
    using It = std::string::const_iterator;
    for (std::string const input : { "foo=bar&baz=boo" })
    {
        std::cout << "======= " << input << "\n";
        It f = input.begin(), l = input.end();
        string_map<std::string> sm;
        if (parse(f, l, query_grammar<It>{}, sm)) {
            std::cout << "Parsed " << sm.size() << " pairs\n";
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}

打印

======= foo=bar&baz=boo
Parsed 2 pairs

Qi改进

以下更简单的语法似乎更好:

<强> Live On Coliru

template <typename Iterator, typename T = std::string>
struct query_grammar : public qi::grammar<Iterator, string_map<T>()>
{
    query_grammar() : query_grammar::base_type(query) {
        using namespace qi;
        pair  =  +~char_("&=") >> '=' >> *~char_("&");
        query = pair % '&';
    }

  private:
    qi::rule<Iterator, std::pair<T,T>()> pair;
    qi::rule<Iterator, std::map<T,T>()> query;
};

它接受空值(例如&q=&x=)和包含其他=&q=7==8&rt=bool的值。它可以显着提高效率(未经测试)。

X3版本

在不查看代码的情况下,我将其直接翻译成X3版本:

<强> Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <iostream>
#include <map>

namespace x3 = boost::spirit::x3;

template <typename T> using string_map = std::map<T, T>;

namespace grammar {
    using namespace x3;
    auto pair  =  +~char_("&=") >> '=' >> *~char_("&");
    auto query = pair % '&';
}

int main() {
    using It = std::string::const_iterator;
    for (std::string const input : { "foo=bar&baz=boo" })
    {
        std::cout << "======= " << input << "\n";
        It f = input.begin(), l = input.end();
        string_map<std::string> sm;
        if (parse(f, l, grammar::query, sm)) {
            std::cout << "Parsed " << sm.size() << " pairs\n";
        } else {
            std::cout << "Parse failed\n";
        }

        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}

其中,显然(---)打印

======= foo=bar&baz=boo
Parsed 2 pairs

X3改进

您可能希望强制规则的属性类型,因为自动属性传播可能具有令人惊讶的启发式。

namespace grammar {

    template <typename T = std::string> auto& query() {
        using namespace x3;

        static const auto s_pair  
            = rule<struct pair_, std::pair<T, T> > {"pair"} 
            = +~char_("&=") >> -('=' >> *~char_("&"));
        static const auto s_query
            = rule<struct query_, std::map<T, T> > {"query"}
            = s_pair % '&';

        return s_query;
    };

}

查看 Live On Coliru

出了什么问题?

X3版本在std::map<>::value_type

中遇到了与const键类型相同的问题