gpt4 book ai didi

c++ - Boost Spirit Qi 验证输入解析器

转载 作者:搜寻专家 更新时间:2023-10-31 01:30:41 25 4
gpt4 key购买 nike

我有一个非常基本的 Boost Spirit Qi 语法来解析 IP 端口或 IP 端口范围,即 "6322""6322-6325" .

语法如下:

  template<class It>
void init_port_rule(u16_rule<It>& port)
{
port = boost::spirit::qi::uint_parser<uint16_t, 10, 2, 5>();
port.name("valid port range: (10, 65535)");
}

typedef boost::fusion::vector
< std::uint16_t
, boost::optional<std::uint16_t>
> port_range_type
;

template<class It>
struct port_range_grammar
: boost::spirit::qi::grammar
< It
, port_range_type()
>
{
typedef typename port_range_grammar::base_type::sig_type signature;

port_range_grammar()
: port_range_grammar::base_type(start, "port_range")
{
init_port_rule(port);
using namespace boost::spirit::qi;
start = port > -(lit("-") > port);
}

private:
boost::spirit::qi::rule<It, signature> start;
boost::spirit::qi::rule<It, std::uint16_t()> port;
};

我有点难以定义,在 port1 范围内必须小于 port2 .我想我必须使用 eps解析器在这里,但似乎没有找到指定它的正确方法。非常欢迎任何建议。

最佳答案

你确实可以使用语义 Action 。不过,您并不总是需要将它们附加到 eps 节点。如果这样做,您会得到以下结果:

port %= uint_parser<uint16_t, 10, 2, 5>() >> eps[ _pass = (_val>=10 && _val<=65535) ];
start = (port >> -('-' >> port)) >> eps(validate(_val));

请注意,一条规则使用 Simple Form eps附有语义 Action 。这需要 operator%=still invoke automatic attribute propagation .

第二个实例使用 Semantic Predicate form of eps . validate 函数需要是 Phoenix Actor,我这样定义它:

struct validations {
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};
boost::phoenix::function<validations> validate;

更通用/一致

请注意,您可以像这样在两条规则上使用第二种规则样式:

port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
start = (port >> -('-' >> port)) >> eps(validate(_val));

如果您只是添加一个重载来验证单个端口:

struct validations {
bool operator()(Port const& port) const {
return port>=10 && port<=65535;
}
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};

第一次测试

让我们定义一些很好的边缘情况并测试它们!

Live On Coliru

#include <boost/fusion/adapted/struct.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;

using Port = std::uint16_t;

struct PortRange {
Port start;
boost::optional<Port> end;
};

BOOST_FUSION_ADAPT_STRUCT(PortRange, start, end)

template <class It, typename Attr = PortRange> struct port_range_grammar : qi::grammar<It, Attr()> {

port_range_grammar() : port_range_grammar::base_type(start, "port_range") {
using namespace qi;

port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
start = (port >> -('-' >> port)) >> eps(validate(_val));

port.name("valid port range: (10, 65535)");
}

private:
struct validations {
bool operator()(Port const& port) const {
return port>=10 && port<=65535;
}
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};
boost::phoenix::function<validations> validate;
qi::rule<It, Attr()> start;
qi::rule<It, Port()> port;
};

int main() {
using It = std::string::const_iterator;
port_range_grammar<It> const g;

std::string const valid[] = {"10", "6322", "6322-6325", "65535"};
std::string const invalid[] = {"9", "09", "065535", "65536", "-1", "6325-6322"};

std::cout << " -------- valid cases\n";
for (std::string const input : valid) {
It f=input.begin(), l = input.end();
PortRange range;
bool accepted = parse(f, l, g, range);
if (accepted)
std::cout << "Parsed '" << input << "' to " << boost::fusion::as_vector(range) << "\n";
else
std::cout << "TEST FAILED '" << input << "'\n";
}

std::cout << " -------- invalid cases\n";
for (std::string const input : invalid) {
It f=input.begin(), l = input.end();
PortRange range;
bool accepted = parse(f, l, g, range);
if (accepted)
std::cout << "TEST FAILED '" << input << "' (returned " << boost::fusion::as_vector(range) << ")\n";
}
}

打印:

 -------- valid cases
Parsed '10' to (10 --)
Parsed '6322' to (6322 --)
Parsed '6322-6325' to (6322 6325)
Parsed '65535' to (65535 --)
-------- invalid cases
TEST FAILED '065535' (returned (6553 --))

CONGRATULATIONS We found a broken edge case

事实证明,通过将 uint_parser 限制为 5 个位置,我们可能会在输入中保留字符,因此 065535 解析为 6553(保留 '5'未解析...)。修复很简单:

start = (port >> -('-' >> port)) >> eoi >> eps(validate(_val));

或者确实是:

start %= (port >> -('-' >> port)) >> eoi[ _pass = validate(_val) ];

固定版本 Live On Coliru

关于属性类型的几句话

您会注意到我修改了您的属性类型。这大部分是“好品味”。请注意,在实践中,您可能希望将范围表示为单端口或范围:

using Port = std::uint16_t;

struct PortRange {
Port start, end;
};

using PortOrRange = boost::variant<Port, PortRange>;

然后你会像这样解析:

port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
range = (port >> '-' >> port) >> eps(validate(_val));

start = (range | port) >> eoi;

完整演示 Live On Coliru

您可能认为这会变得难以使用。 我同意!

改为简化

让我们首先不使用 variantoptional。让我们让一个端口只是一个范围恰好有start==end:

using Port = std::uint16_t;

struct PortRange {
Port start, end;
};

像这样解析它:

start = port >> -('-' >> port | attr(0)) >> eoi >> eps(validate(_val));

我们在validate 中所做的就是检查end 是否为0:

    bool operator()(PortRange& range) const {
if (range.end == 0)
range.end = range.start;
return range.start <= range.end;
}

现在输出是: Live On Coliru

 -------- valid cases
Parsed '10' to (10-10)
Parsed '6322' to (6322-6322)
Parsed '6322-6325' to (6322-6325)
Parsed '65535' to (65535-65535)
-------- invalid cases

请注意,您现在如何始终枚举 start..end 而不知道是否存在端口或端口范围。这可能很方便(取决于您正在实现的逻辑)。

关于c++ - Boost Spirit Qi 验证输入解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47243884/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com