Boost.spirit:解析数字char和string

Arl*_*len 2 c++ boost-spirit boost-phoenix boost-spirit-qi

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

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

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

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

seh*_*ehe 9

你可能没有意识到的是,在存在语义动作的情况下,解析器表达式停止了自动属性传播*.

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

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

[ref(num) = _1]   // this is a Semantic Action
Run Code Online (Sandbox Code Playgroud)

所以,最简单的方法来解决这个问题,将是传播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
Run Code Online (Sandbox Code Playgroud)

或者,解决一些偏离主题的问题,甚至更清洁:

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

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

在Coliru上看到它的现场演示(链接)

有很多魔法在进行,这在实践中导致了我的经验法则:

除非你绝对必须,否则避免在Spirit Qi表达式中使用语义动作

我之前对此有所了解,特别是对此有一个答案:提升精神:"语义行为是邪恶的"?

根据我的经验,使用属性自定义点来调整自动传播几乎总是更清晰,而不是放弃自动规则并采用手动属性处理.


1传播这些属性的技术上发生的是,num并且parsed_str将作为Fusion序列与整个解析表达式"绑定":

fusion::vector2<unsigned&, std::string&>
Run Code Online (Sandbox Code Playgroud)

和规则的暴露属性:

fusion::vector2<unsigned, std::vector<char> >
Run Code Online (Sandbox Code Playgroud)

将在转让期间"转变"为.属性兼容性规则允许此转换以及许多其他规则.


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

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

这里有一些细微之处:

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

  • 我们需要资格phx::ref(parsed_str),因为即使using boost::phoenix::ref将不足以消除歧义std::refphx::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);
    
    Run Code Online (Sandbox Code Playgroud)

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