boost::spirit::qi对输出进行重复解析

boost::spirit::qi duplicate parsing on the output

本文关键字:输出 spirit qi boost      更新时间:2023-10-16

我有一个非常简单的解析器,使用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

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

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_stringqi::rawqi::lexemeqi::no_skip
  • 语义操作(不依赖自动规则)

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

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

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

希望这能有所帮助。

完整示例:Live onhttp://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

我曾经遇到过类似的问题。这是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;

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