5

我有一个使用 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

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

4

2 回答 2

8

qi::hold正如@Andrzej 正确提到的那样,这是一种方法

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


关键是,Spirit 不会通过设计要求对属性进行“临时”存储。事实上,它不能真正假设属性是可复制的。这就是这里的原因(想象将所有内容解析为单个 std::vector<> 并为每个解析器步骤进行复制?)。

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

我的看法是

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

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

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

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

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

  • 负前瞻 ( !parser) - 不影响属性
  • 正向前瞻 ( &parser) - 不影响属性
  • 熟悉qi::as_string, qi::raw,qi::lexemeqi::no_skip
  • 语义动作(不要依赖自动规则

以下是对原始规则的最小更改:

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

这与使用 _hold_ing 属性值的代码具有大致相同的效果,qi::hold但没有性能缺陷。

希望这可以帮助。

完整示例:在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:\t" << std::boolalpha 
                  << parse(std::begin(input), std::end(input), zeroTo255, output) 
                  << ": " << output << std::endl;

        output.clear();
        std::cout << "alternatively:\t" << 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
于 2012-12-14T08:40:42.800 回答
5

我曾经遇到过类似的问题。这是 Spirit 中替代运算符的特殊工作方式。如果您使用附加指令“hold”,您的示例应该可以工作。

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

有关此行为的详细信息,请参阅此线程

于 2012-12-13T23:05:25.747 回答