[英]Misunderstanding repeat directive - it should fail, but doesn't
我想写一个语法(高度简化):
grr := integer [ . integer ]
和
integer ::= digit { [ underline ] digit }
由于稍后再次需要解析的文字(真正的语法更复杂,并非所有内容都可以立即转换为数字),因此文字必须完全存储为 AST 中的字符串(更准确地说是 iterator_range)以供以后使用(带下划线) .
现在的问题是文字表达式可能比它们应该的更长(关于稍后的实现/计算等)。 显而易见的解决方案是repeat
指令(这里详细说明 Qi 重复或X3非常短)。
这是我的问题开始的地方( coliru ):
for(std::string_view const s : {
// ok
"0", "10", "1_0", "012345",
// too long
"0123456",
"1_2_3_4_5_6_7_8_9_0",
// absolutely invalid
"1_2_3_4_5_6_", "_0123_456", ""
}) {
auto const cs = x3::char_("0-9");
std::string attr;
bool const ok = x3::parse(std::begin(s), std::end(s),
x3::raw[ cs >> x3::repeat(0, 5)[ ('_' >> cs) | cs] ],
attr);
cout << s << " -> " << attr
<< " (" << std::boolalpha << ok << ")"
<< "\n";
}
给
0 -> 0 (true)
10 -> 10 (true)
1_0 -> 1_0 (true)
012345 -> 012345 (true)
0123456 -> 012345 (true)
1_2_3_4_5_6_7_8_9_0 -> 1_2_3_4_5_6 (true)
1_2_3_4_5_6_ -> 1_2_3_4_5_6 (true)
_0123_456 -> (false)
-> (false)
如果文字太长,解析器应该会失败,但它不会。 如果它以下划线结尾,它也应该这样做——但事实并非如此。 开头的下划线和空文字被正确识别/解析为假。
同时,我尝试将更复杂的解析器写入单独的解析器类,但在这里我错过了识别以下划线结尾的文字的规则......
此外, BOOST_SPIRIT_X3_DEBUG 似乎突然坏了 - 没有输出。
我的问题的解决方案是什么? 除了通过迭代器,计数器等绝对低级和复杂之外,我没有想法。
这个问题也会影响其他要实施的规则。
如果文字太长,解析器应该会失败
它在哪里说呢? 看起来代码完全符合您的要求:它最多解析 6 位数字,并带有必要的下划线。 输出甚至证实它确实做到了。
您当然可以通过显示未解析的内容来使其更加明显:
auto f = begin(s), l = end(s);
bool const ok = x3::parse(
f, l, x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]], attr);
fmt::print(
"{:21} -> {:5} {:13} remaining '{}'\n",
fmt::format("'{}'", s),
ok,
fmt::format("'{}'", attr),
std::string(f, l));
印刷
'0' -> true '0' remaining ''
'10' -> true '10' remaining ''
'1_0' -> true '1_0' remaining ''
'012345' -> true '012345' remaining ''
'0123456' -> true '012345' remaining '6'
'1_2_3_4_5_6_7_8_9_0' -> true '1_2_3_4_5_6' remaining '_7_8_9_0'
'1_2_3_4_5_6_' -> true '1_2_3_4_5_6' remaining '_'
'_0123_456' -> false '' remaining '_0123_456'
'' -> false '' remaining ''
要断言解析完整的输入,请使用x3::eoi
或检查迭代器:
bool const ok = x3::parse(
f,
l,
x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]] >> x3::eoi,
attr);
印刷
'0' -> true '0' remaining ''
'10' -> true '10' remaining ''
'1_0' -> true '1_0' remaining ''
'012345' -> true '012345' remaining ''
'0123456' -> false '012345' remaining '0123456'
'1_2_3_4_5_6_7_8_9_0' -> false '1_2_3_4_5_6' remaining '1_2_3_4_5_6_7_8_9_0'
'1_2_3_4_5_6_' -> false '1_2_3_4_5_6' remaining '1_2_3_4_5_6_'
'_0123_456' -> false '' remaining '_0123_456'
'' -> false '' remaining ''
相反,如果您想允许输入继续,而不是某些字符,例如解析许多这样的“数字”:
auto const number = x3::lexeme[ //
x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
// within the lexeme, assert that no digit or _ follows
>> ! (cs | '_') //
];
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
using namespace std::string_view_literals;
namespace Parser {
namespace x3 = boost::spirit::x3;
auto const cs = x3::digit;
auto const number = x3::lexeme[ //
x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
// within the lexeme, assert that no digit or _ follows
>> ! (cs | '_') //
];
auto const ws_or_comment = x3::space | "//" >> *~x3::char_("\r\n");
auto const numbers = x3::skip(ws_or_comment)[number % ','];
} // namespace Parser
int main()
{
std::vector<std::string> attr;
std::string_view const s =
R"(0,
10,
1_0,
012345,
// too long
0123456,
1_2_3_4_5_6_7_8_9_0,
// absolutely invalid
1_2_3_4_5_6_,
_0123_456)"sv;
auto f = begin(s), l = end(s);
bool const ok = parse(f, l, Parser::numbers, attr);
fmt::print("{}: {}\nremaining '{}'\n", ok, attr, std::string(f, l));
}
印刷
true: ["0", "10", "1_0", "012345"]
remaining ',
// too long
0123456,
1_2_3_4_5_6_7_8_9_0,
// absolutely invalid
1_2_3_4_5_6_,
_0123_456'
在存在其他无关紧要的空白的情况下,要深入检查词位内部的要点:
auto const numbers = x3::skip(ws_or_comment)[*number];
稍微调整一下测试输入(去掉逗号):
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
using namespace std::string_view_literals;
namespace Parser {
namespace x3 = boost::spirit::x3;
auto const cs = x3::digit;
auto const number = x3::lexeme[ //
x3::raw[cs >> x3::repeat(0, 5)[('_' >> cs) | cs]]
// within the lexeme, assert that no digit or _ follows
>> ! (cs | '_') //
];
auto const ws_or_comment = x3::space | "//" >> *~x3::char_("\r\n");
auto const numbers = x3::skip(ws_or_comment)[*number];
} // namespace Parser
int main()
{
std::vector<std::string> attr;
std::string_view const s =
R"(0
10
1_0
012345
// too long
0123456
1_2_3_4_5_6_7_8_9_0
// absolutely invalid
1_2_3_4_5_6_
_0123_456)"sv;
auto f = begin(s), l = end(s);
bool const ok = parse(f, l, Parser::numbers, attr);
fmt::print("{}: {}\nremaining '{}'\n", ok, attr, std::string(f, l));
}
印刷
true: ["0", "10", "1_0", "012345"]
remaining '0123456
1_2_3_4_5_6_7_8_9_0
// absolutely invalid
1_2_3_4_5_6_
_0123_456'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.