了解 Boost.Spirit 中的列表运算符 (%)

2024-02-14

你能帮我理解两者之间的区别吗a % b解析器及其扩展a >> *(b >> a)Boost.Spirit 中的形式?虽然参考手册 http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/qi/reference/operator/list.html表明它们是等价的,

列表运算符,a % b, 是一个二元运算符,它匹配一个或多个重复项的列表a由出现的b。这相当于a >> *(b >> a).

以下程序根据使用的不同产生不同的结果:

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>

struct Record {
  int id;
  std::vector<int> values;
};

BOOST_FUSION_ADAPT_STRUCT(Record,
  (int, id)
  (std::vector<int>, values)
)

int main() {
  namespace qi = boost::spirit::qi;

  const auto str = std::string{"1: 2, 3, 4"};

  const auto rule1 = qi::int_ >> ':' >> (qi::int_ % ',')                 >> qi::eoi;
  const auto rule2 = qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_)) >> qi::eoi;

  Record record1;
  if (qi::phrase_parse(str.begin(), str.end(), rule1, qi::space, record1)) {
    std::cout << record1.id << ": ";
    for (const auto& value : record1.values) { std::cout << value << ", "; }
    std::cout << '\n';
  } else {
    std::cerr << "syntax error\n";
  }

  Record record2;
  if (qi::phrase_parse(str.begin(), str.end(), rule2, qi::space, record2)) {
    std::cout << record2.id << ": ";
    for (const auto& value : record2.values) { std::cout << value << ", "; }
    std::cout << '\n';
  } else {
    std::cerr << "syntax error\n";
  }
}

住在科利鲁 http://coliru.stacked-crooked.com/a/f11b90c4dc24d85f

1: 2, 3, 4, 
1: 2, 

rule1 and rule2不同之处仅在于rule1使用列表运算符 ((qi::int_ % ',')) and rule2使用其扩展形式((qi::int_ >> *(',' >> qi::int_)))。然而,rule1产生的1: 2, 3, 4,(如预期)和rule2产生的1: 2,。我无法理解的结果rule2: 1) 为什么它与rule12)为什么3 and 4不包括在record2.values虽然phrase_parse以某种方式返回 true 吗?


UpdateX3版本added https://stackoverflow.com/a/33817135/85371

首先,你陷入了一个很深的陷阱:

Qi 规则不适用于auto. Use qi::copy或者刚刚使用过qi::rule<>。你的程序有未定义的行为,实际上它对我来说崩溃了(valgrind 指出了悬空引用的起源)。

所以,首先:

const auto rule = qi::copy(qi::int_ >> ':' >> (qi::int_ % ',')                 >> qi::eoi); 

现在,当您删除程序中的冗余时,您将得到:

重现问题

Live On Coliru http://coliru.stacked-crooked.com/a/5cada4939db63b12

int main() {
    test(qi::copy(qi::int_ >> ':' >> (qi::int_ % ',')));
    test(qi::copy(qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_))));
}

Printing

1: 2, 3, 4, 
1: 2, 

原因和解决方法

发生了什么事3, 4这是解析成功?

嗯,属性传播规则表明qi::int_ >> *(',' >> qi::int_)暴露了一个tuple<int, vector<int> >。为了神奇地 DoTheRightThing(TM) Spirit 意外失火并“分配”int进入属性引用,忽略剩余的vector<int>.

如果您想让容器属性解析为“原子组”,请使用qi::as<>:

test(qi::copy(qi::int_ >> ':' >> qi::as<Record::values_t>() [ qi::int_ >> *(',' >> qi::int_)]));

Here as<>充当属性兼容性启发式的障碍,语法知道你的意思:

Live On Coliru http://coliru.stacked-crooked.com/a/8db6c5bef6e4b548

#include <iostream>
#include <string>
#include <vector>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>

struct Record {
  int id;
  using values_t = std::vector<int>;
  values_t values;
};

BOOST_FUSION_ADAPT_STRUCT(Record, id, values)

namespace qi = boost::spirit::qi;

template <typename T>
void test(T const& rule) {
    const std::string str = "1: 2, 3, 4";

    Record record;

    if (qi::phrase_parse(str.begin(), str.end(), rule >> qi::eoi, qi::space, record)) {
        std::cout << record.id << ": ";
        for (const auto& value : record.values) { std::cout << value << ", "; }
        std::cout << '\n';
    } else {
        std::cerr << "syntax error\n";
    }
}

int main() {
    test(qi::copy(qi::int_ >> ':' >> (qi::int_ % ',')));
    test(qi::copy(qi::int_ >> ':' >> (qi::int_ >> *(',' >> qi::int_))));
    test(qi::copy(qi::int_ >> ':' >> qi::as<Record::values_t>() [ qi::int_ >> *(',' >> qi::int_)]));
}

Prints

1: 2, 3, 4, 
1: 2, 
1: 2, 3, 4, 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解 Boost.Spirit 中的列表运算符 (%) 的相关文章

随机推荐