【问题标题】:Boost Spirit Qi: Omit element in Kleene Star parserBoost Spirit Qi:在 Kleene Star 解析器中省略元素
【发布时间】:2013-11-18 00:42:54
【问题描述】:

我想解析特殊结构并将其余部分丢弃。但我不想使用船长。

我想获得这些结构的向量,所以我使用 Kleene Star 解析器作为主要规则。但是,每次丢弃某些东西时,都会在向量中插入一个默认构造元素。

这是一个虚构的例子。它只是寻找字符串Test 并将其余的扔掉,至少这是计划。但是每次规则 garbage 成功时,它都会向规则 all 中的向量添加一个默认构造项,输出为 7,而不是 1。如果规则 @987654324,我如何告诉 Spirit 只添加到向量中@成功了吗?

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>

#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

namespace qi = boost::spirit::qi;

struct container {
  std::string name;
  bool        dummy;
};
BOOST_FUSION_ADAPT_STRUCT(::container,
                          (std::string, name)
                          (bool, dummy))

int main() {
  typedef std::string::const_iterator iterator;

  qi::rule<iterator, std::vector<container>()> all;
  qi::rule<iterator, container()> item;
  qi::rule<iterator, std::string()> string_rule;
  qi::rule<iterator> garbage;

  all = *(garbage | item);
  garbage = qi::char_ - qi::lit("Test");
  string_rule = qi::string("Test");
  item = string_rule >> qi::attr(true);

  std::vector<container> ast;

  std::string input = "blaTestbla";

  iterator first = input.begin();
  iterator last = input.end();

  bool result = qi::parse(first, last, all, ast);
  if (result) {
    result = first == last;
  }

  if (result) {
    std::cout << "Parsed " << ast.size() << " element(s)" << std::endl;
  } else {
    std::cout << "failure" << std::endl;
  }

}

【问题讨论】:

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


    【解决方案1】:

    由于 sehe 的回答或多或少是出于教育目的,我们现在有几种解决方案:

    *garbage >> -(item % *garbage) >> *garbage
    
    *garbage >> *(item >> *garbage)
    
    all = *(garbage | item[phx::push_back(qi::_val,qi::_1)]);
    

    还有来自 cv_and_he 的解决方案:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    
    #include <boost/config/warning_disable.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    #include <iostream>
    #include <string>
    #include <vector>
    
    namespace qi = boost::spirit::qi;
    
    struct container {
      std::string name;
      bool        dummy;
    };
    BOOST_FUSION_ADAPT_STRUCT(::container,
                              (std::string, name)
                              (bool, dummy))
    
    struct container_vector {   //ADDED
        std::vector<container> data;
    };
    
    namespace boost{ namespace spirit{ namespace traits //ADDED
    {
        template <>
        struct is_container<container_vector> : boost::mpl::true_ {};
    
        template <>
        struct container_value<container_vector> {
            typedef optional<container> type;
        };
    
        template <>
        struct push_back_container<container_vector,optional<container> > {
            static bool call(container_vector& cont, const optional<container>& val) {
                if(val)
                    cont.data.push_back(*val);
                return true;
            }
        };
    }}}
    
    int main() {
      typedef std::string::const_iterator iterator;
    
      qi::rule<iterator, container_vector()> all; //CHANGED
      qi::rule<iterator, container()> item;
      qi::rule<iterator, std::string()> string_rule;
      qi::rule<iterator> garbage;
    
      all = *(garbage | item);
      garbage = qi::char_ - qi::lit("Test");
      string_rule = qi::string("Test");
      item = string_rule >> qi::attr(true);
    
      container_vector ast;     //CHANGED
    
      std::string input = "blaTestbla";
    
      iterator first = input.begin();
      iterator last = input.end();
    
      bool result = qi::parse(first, last, all, ast);
      if (result) {
        result = first == last;
      }
    
      if (result) {
        std::cout << "Parsed " << ast.data.size() << " element(s)" << std::endl;   //CHANGED 
      } else {
        std::cout << "failure" << std::endl;
      }
    
    }
    

    虽然我不想使用船长,但我最终还是选择了:

    start = qi::skip(garbage.alias())[*item];
    

    在我使用 Linux 内核的 c 文件和我的生产规则进行的非科学测试中,最后一个解决方案是最快的(提高了 1-2%)。

    【讨论】:

    • 仍然是一个令人印象深刻的答案。我已经忘记了其中一些事情。会再次 +1。
    【解决方案2】:

    快速修复(不一定是最高效的)是

    all         = -(item - garbage) % +garbage;
    

    打印出来:

    Parsed 3 element(s)
    

    Live on Coliru

    【讨论】:

    • 谢谢,但它应该只找到一个元素。但是你的回答让我思考;我的问题是|&gt;&gt; 的传播类型不同。这导致规则 *garbage &gt;&gt; -(item % *garbage) &gt;&gt; *garbage 或更短的 *garbage &gt;&gt; *(item &gt;&gt; *garbage) 给出所需的结果。
    • @Mike Cheers,这就是我打算让你开始的。
    • :-D(还有一些文字,因为不允许单独使用笑脸)
    • @MikeM 你可能应该把它作为一个答案(并接受它),除非他编辑他的答案。就目前而言,接受的答案不起作用。 This 是基于this mailing list thread 的一种可能过于复杂的替代方案。最简单的方法是使用语义操作:all = *(garbage | item[phx::push_back(qi::_val,qi::_1)]);.
    • @cv_and_he 感谢您采用完全不同的方法。我也添加了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    相关资源
    最近更新 更多