boost::spirit::qi 重复解析输出

2021-12-24 00:00:00 c++ boost boost-spirit boost-spirit-qi

我有一个使用 Boost::Spirit 的非常简单的解析器:

I have this very simple parser using Boost::Spirit:

rule<std::string::iterator, std::string()> zeroTo255 = (string("25") >> char_('0', '5'))
    | (char_('2') >> char_('0', '4') >> digit)
    | (char_('1') >> repeat[2](digit))
    | (char_('1', '9') >> digit) | digit;

当我尝试解析时

std::string o{"1"};
std::string s;
parse(o.begin(), o.end(), zeroTo255, s);
std::cout << o << ": " << s << std::endl;

我有作为输出

1: 111

我显然做错了什么,但是什么?

I'm obviously doing something wrong, but what?

推荐答案

qi::hold 是一种解决方案,正如@Andrzej 正确提到的

qi::hold is one way about it, as correctly mentioned by @Andrzej

我想我有一些观察可能会有所帮助,以及更好的解决方案.

I think I have a few observations that might help, as well as a better solution.

关键是 Spirit 设计上不需要属性的临时"存储.事实上,它不能真正假设该属性首先是可复制的.这就是这里的原因(想象一下将所有内容解析为单个 std::vector<> 并为每个解析器步骤复制?).

The point is that Spirit will not require 'temp' storage for attributes by design. In fact, it can't really assume the attribute be copyable in the first place. This is the reason here (imagine parsing everything into a single std::vector<> and copying for each parser step?).

在更重要的层面上,在我看来,这里倒退的不是属性处理,而是解析器表达式本身:它没有说明意图,并导致各种处理数字表示的复杂性当......真的不应该.

On a more essential level, it looks to me as if it is not the attribute handling that is backwards here, but the parser expression itself: It fails to state the intent, and incurs all kinds of complexity dealing with number representations when... really it shouldn't.

我的看法是

rule<std::string::iterator, std::string()> zeroTo255, alternatively;

alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];

你看:你让 Spirit 解析一个数字,实际上只是验证范围,这就是你首先想要做的.

You see: you let Spirit parse a number, and indeed just verify the range, which is what you wanted to do in the first place.

让我印象深刻的第二件事是规则公开了一个 std::string 属性,而不是 unsigned char 例如这是为什么?

The second thing that strikes me as a-typical, is the fact that the rule exposes a std::string attribute, instead of unsigned char e.g. Why is that?

假设这是一个有意识的设计决定,您可以通过明智地使用

Assuming this was a conscious design decision, you can have it your way by judicious use of

  • 否定前瞻 (!parser) - 不影响属性
  • 正向前瞻 (&parser) - 不影响属性
  • 熟悉qi::as_stringqi::rawqi::lexemeqi::no_skip
  • 语义动作(不要依赖自动规则)
  • negative lookahead (!parser) - which doesn't affect attributes
  • positive lookahead (&parser) - which doesn't affect attributes
  • Get acquainted with qi::as_string, qi::raw, qi::lexeme and qi::no_skip
  • semantic actions (don't rely on automatic rules)

以下是对您的原始规则进行的最小改动本可以起作用:

Here's what minimal change to your original rule would have worked:

zeroTo255 = raw [ 
          ("25" >> char_("0-5"))
        | ('2' >> char_("0-4") >> digit)
        | ('1' >> digit >> digit)
        | (char_("1-9") >> digit) 
        | digit
    ];

这与使用 qi::hold 的代码效果大致相同,但不是 _hold_ing 属性值的性能缺陷.

This has roughly the same effect as the code using qi::hold but not the performance drawback of _hold_ing attribute values.

希望这会有所帮助.

完整示例:http://liveworkspace.org/code/4v4CQW$0:

Full sample: Live on http://liveworkspace.org/code/4v4CQW$0:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;

int main()
{
    using namespace qi;
    rule<std::string::iterator, std::string()> zeroTo255, alternatively;

    zeroTo255 = raw [ 
              ("25" >> char_("0-5"))
            | ('2' >> char_("0-4") >> digit)
            | ('1' >> digit >> digit)
            | (char_("1-9") >> digit) 
            | digit
        ];

    alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];

    for (auto& input : std::vector<std::string> { "255", "249", "178", "30", "4" })
    {
        std::string output;
        std::cout << "zeroTo255:	" << std::boolalpha 
                  << parse(std::begin(input), std::end(input), zeroTo255, output) 
                  << ": " << output << std::endl;

        output.clear();
        std::cout << "alternatively:	" << std::boolalpha 
                  << parse(std::begin(input), std::end(input), alternatively, output) 
                  << ": " << output << std::endl;
    }

}

输出

zeroTo255:      true: 255
alternatively:  true: 255
zeroTo255:      true: 249
alternatively:  true: 249
zeroTo255:      true: 178
alternatively:  true: 178
zeroTo255:      true: 30
alternatively:  true: 30
zeroTo255:      true: 4
alternatively:  true: 4

相关文章