2

我需要解析一行,其中包含一个 unsigned int、X要丢弃的字符和一个字符串,所有这些都由一个或多个空格分隔。例如,1234 X abcd

bool a = qi::phrase_parse(first, last,
      uint_[ref(num) = _1] >> lit('X') >> lexeme[+(char_ - ' ')],
      space, parsed_str);

上面的代码解析了这三个部分,但字符串最终包含一个垃圾字符 ( �abcd) 并且大小为 5 而不是 4。

我的解析器有什么问题?为什么字符串中有垃圾?

4

1 回答 1

9

您可能没有意识到,解析器表达式在存在语义动作*时停止自动属性传播

*文档背景:规则如何传播其属性?

您正在使用语义操作来“手动”传播uint_解析器的属性:

[ref(num) = _1]   // this is a Semantic Action

所以解决这个问题的最简单方法也是num自动传播(qi::parseqi::phrase_parseAPI 的预期方式):

bool ok = qi::phrase_parse(first, last,               // input iterators
        uint_ >> lit('X') >> lexeme[+(char_ - ' ')],  // parser expr
        space,                                      // skipper
        num, parsed_str);                            // output attributes

或者,解决一些题外话,甚至更干净:

bool ok = qi::phrase_parse(first, last,
        uint_ >> 'X' >> lexeme[+graph],
        blank, 
        num, parsed_str);

如您所见,您可以传递多个左值作为输出属性接收者。1, 2

在 Coliru 上观看现场演示(链接)

有很多魔法在发生,这实际上导致了我的经验法则:

除非万不得已,否则避免在灵气表达中使用语义动作

我以前有过这个,在一个专门关于这个的答案中:Boost Spirit:“语义行为是邪恶的”?

根据我的经验,使用属性自定义点来调整自动传播几乎总是比放弃自动规则并求助于手动属性处理更干净。


1传播这些属性在技术上发生的事情是,num并将parsed_str作为融合序列“绑定”到整个解析表达式:

fusion::vector2<unsigned&, std::string&>

以及规则的暴露属性:

fusion::vector2<unsigned, std::vector<char> >

将在分配期间“转换”为该值。属性兼容性规则允许这种转换以及许多其他转换。


2或者,对两者都使用语义操作:

bool ok = qi::phrase_parse(first, last,
        (uint_ >> 'X' >> as_string [ lexeme[+graph] ]) 
            [ phx::ref(num) = _1, phx::ref(parsed_str) = _2 ],
        blank);

这里有一些微妙之处:

  • 我们需要as_string在这里公开属性std::string而不是std::vector<char>(见上文)

  • 我们需要限定phx::ref(parsed_str),因为 evenusing boost::phoenix::ref不足以消除歧义std::ref并且phx::ref: ADL 将被拖进来,std::ref因为它与parsed_str.

  • 对语义操作进行分组以防止部分分配的结果,例如,num即使X输入中可能缺少以下内容,也会覆盖:

    bool ok = qi::phrase_parse(first, last,
           uint_ [ phx::ref(num) = _1 ] 
        >> 'X' 
        >> as_string [ lexeme[+graph] ] [ phx::ref(parsed_str) = _1 ],
        blank);
    

如果您避免手动属性传播,所有这些复杂性都可以隐藏在您的视野中!

于 2013-07-06T10:55:49.767 回答