【问题标题】:boost::spirit::qi rule reduce parsing errorboost::spirit::qi 规则减少解析错误
【发布时间】:2016-01-20 09:52:06
【问题描述】:

我尝试使用boost::spirit::qi 解析integer 和double 对的列表。 integer 在此列表之前和之后。此列表的一个示例是:

20 1 1.3 2 2.3 30

我要创建的结构是:

typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
  int first;
  std::vector< std::pair<int, double> > list;
  int last;
};

我还使用qi::on_error 向我的解析器添加了错误处理,并将所有序列&gt;&gt; 转换为期望&gt;。

我的实现是:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

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

namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

typedef std::vector< std::pair<int, double> > vec_t;
struct list_s{
  int first;
  vec_t list;
  int last;
};

BOOST_FUSION_ADAPT_STRUCT(
    list_s,
    (int, first)
    (vec_t, list)
    (int, last)
)

template <typename Iterator>
struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type >
{
  public:
    list_parser() : list_parser::base_type(r) {
      r = qi::eps >>
        (
          qi::int_
          > *( qi::int_ > qi::double_ )
          > qi::int_
        );

      r.name("r");
      qi::on_error<qi::fail>
        (
         r
         , std::cout
         << phoenix::val("\nError! Expecting ")
         << qi::labels::_4 
         << phoenix::val(" here: \"")
         << phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
         << phoenix::val("\"\n\n")
        );
    }
  private:
    qi::rule<Iterator, list_s(), ascii::space_type > r;
};

int main() {
  std::string str("20 1 1.3 2 2.3 30");

  list_s r;
  list_parser<std::string::const_iterator> p;
  std::string::const_iterator iter = str.begin();
  std::string::const_iterator end = str.end();
  if( qi::phrase_parse(iter, end, p, ascii::space, r) ){
    std::cout << r.first << std::endl;
    for(auto& i : r.list) std::cout << i.first << " , " << i.second << std::endl;
    std::cout << r.last << std::endl;
  }
  return 0;
}

运行此示例时,我得到 ​​p>

Error! Expecting <real> here: ""

我知道我可以将列表解析规则更改为 sequence

*( qi::int_ >> qi::double_ )

克服上述错误。但是随后20 1 1.3 2 error 30的解析也成功了。结果是:

20
1 , 1.3
2

有什么解决这个问题的建议吗?

【问题讨论】:

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


    【解决方案1】:

    期望点产生非回溯期望。

    所以你不能接受失败后的替代分支。

    这意味着您的最后一个 int 将与 (int_ &gt; double_) 中的 int 匹配。但是,该表达式需要 (&gt;) 后跟double_,因为它不需要,所以它会抛出。

    替换为&gt; &gt;&gt;。

    事实上,看起来你只是想确保整个输入/行都被消耗掉,所以让你的期望显式:

        r = (
                qi::int_
                > *( qi::int_ >> qi::double_ )
                > qi::int_
            )
            > qi::eoi
            ;
    

    演示

    Live On Coliru

    Note: also enabled rule debug (implicitly names the rule):
    
    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/fusion/include/adapted.hpp>
    
    #include <iostream>
    
    namespace phoenix = boost::phoenix;
    namespace qi      = boost::spirit::qi;
    namespace ascii   = boost::spirit::ascii;
    
    typedef std::vector<std::pair<int, double>> vec_t;
    
    struct list_s{
      int first;
      vec_t list;
      int last;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(list_s, first, list, last)
    
    template <typename Iterator>
    struct list_parser : qi::grammar<Iterator, list_s(), ascii::space_type>
    {
      public:
        list_parser() : list_parser::base_type(r) {
            r = (
                    qi::int_
                    > *( qi::int_ >> qi::double_ )
                    > qi::int_
                )
                > qi::eoi
                ;
    
          BOOST_SPIRIT_DEBUG_NODES((r))
    
          qi::on_error<qi::fail>
            (
                r
                , std::cout
                << phoenix::val("\nError! Expecting ")
                << qi::labels::_4 
                << phoenix::val(" here: \"")
                << phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
                << phoenix::val("\"\n\n")
            );
        }
      private:
        qi::rule<Iterator, list_s(), ascii::space_type > r;
    };
    
    int main() {
        std::string str("20 1 1.3 2 2.3 30");
    
        using It = std::string::const_iterator;
    
        list_parser<It> p;
        It iter = str.begin(), end = str.end();
    
        list_s data;
        if (qi::phrase_parse(iter, end, p, ascii::space, data)){
            std::cout << data.first << "\n";
    
            for(auto& i : data.list) 
                std::cout << i.first << ", " << i.second << "\n";
    
            std::cout << data.last << "\n";
        }
    }
    

    打印:

    20
    1, 1.3
    2, 2.3
    30
    

    并调试输出:

    <r>
        <try>20 1 1.3 2 2.3 30</try>
        <success></success>
        <attributes>[[20, [[1, 1.3], [2, 2.3]], 30]]</attributes>
    </r>
    

    对于错误输入:

    Live On Coliru

    Error! Expecting <eoi> here: "error 30"
    

    带调试输出:

    <r>
      <try>20 1 1.3 2 error 30</try>
      <fail/>
    </r>
    

    【讨论】:

    • 提问者已经尝试过了,但不喜欢它接受像20 1 1.3 2 error 30 这样的输入(对于“接受”的定义)。
    • 当“接受”意味着parse 返回真时,它确实如此。我认为他/她需要一个分隔符或使用前瞻。
    • 它没有。仔细阅读。 (在答案文本中澄清)
    • Another approach(更丑,可能性能更差,错误信息稍好一点,恕我直言)。
    • 怎么样?为什么?重试不会改变规则或输入。您可以根据业务逻辑在某处创建有条件的eps 来决定要生成哪个分支/消息
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多