Boost spirit 跳过至少有一个空格的解析器
Boost spirit skip parser with at least one whitespace
在我正在实现的语法中,有些元素由白色分隔space。使用跳过解析器,元素之间的 spaces 会自动跳过,但这也允许 no space,这不是我想要的。当然,我可以明确地编写一个包含这些 space 的语法,但在我看来(由于 spirit 提供的复杂性和灵活性)有更好的方法来做到这一点。在那儿?
这是一个例子:
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main(int argc, char** argv)
{
if(argc != 2)
{
std::exit(1);
}
std::string str = argv[1];
auto iter = str.begin();
bool r = qi::phrase_parse(iter, str.end(), qi::char_ >> qi::char_, qi::blank);
if (r && iter == str.end())
{
std::cout << "parse succeeded\n";
}
else
{
std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
}
}
这允许 ab
以及 a b
。我只想允许后者。
与此相关:跳过解析器究竟是如何工作的?一个提供类似 qi::blank 的东西,那么 kleene star 是否应用于形成跳过解析器?我想在这里得到一些启示,也许这也有助于解决这个问题。
附加信息:我的真实解析器看起来像这样:
one = char_("X") >> repeat(2)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
two = char_("Y") >> repeat(3)[omit[+blank] >> +alnum];
three = char_("Z") >> repeat(4)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
main = one | two | three;
这使得语法非常嘈杂,我想避免这种情况。
首先,我通常在(总是?)RFC 中看到这种要求的语法规范。在 99% 的情况下没有问题,考虑例如:
myrule = skip(space) [ uint_ >> uint_ ];
这已经隐含地要求数字之间至少有 1 个空白字符,原因很简单,否则会有 1 个数字。同样的简化发生在很多情况下令人惊讶(例如,参见上周这个答案中围绕无处不在的 WSP 产品所做的简化 )。
除此之外,根据定义,skippers 应用零次或多次,因此没有办法通过 skip()
等现有状态指令获得您想要的内容。另请参阅 or the docs - 在 lexeme
、[no_]skip
和 skip_flag::dont_postskip
下)。
看你的具体语法,我会这样做:
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
在这里,您可以在词位内添加一个否定的先行断言来断言 "the end of the token was reached" - 在您的解析器中将被强制为 !qi::graph
:
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
观看演示:
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::string(iter, str.end());
}
std::cout << std::endl;
}
}
版画
--- "ab" --- parse failed. Remaining unparsed: ab
--- " ab " --- parse failed. Remaining unparsed: ab
--- " a b " --- parse succeeded.
--- "a b" --- parse succeeded.
奖金审查笔记
我的指导方针是:
- 你的船长应该是语法的负责人。令人遗憾的是,所有 Qi 样本都让人们相信您需要让调用者决定
- 结束迭代器检查不等于错误检查。很有可能parse things correctly without consuming all input。这就是为什么报告 "remaining input" 不应该只发生在解析失败的情况下。
- 如果尾随未解析的输入 是 错误,spell it out:
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b happy trees are trailing" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::parse(iter, end, qi::skip(qi::space) [ token >> token >> qi::eoi ]);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::quoted(std::string(iter, str.end()));
}
std::cout << std::endl;
}
}
版画
--- "ab" --- parse failed. Remaining unparsed: "ab"
--- " ab " --- parse failed. Remaining unparsed: " ab "
--- " a b " --- parse succeeded.
--- "a b happy trees are trailing" --- parse failed. Remaining unparsed: "a b happy trees are trailing"
在我正在实现的语法中,有些元素由白色分隔space。使用跳过解析器,元素之间的 spaces 会自动跳过,但这也允许 no space,这不是我想要的。当然,我可以明确地编写一个包含这些 space 的语法,但在我看来(由于 spirit 提供的复杂性和灵活性)有更好的方法来做到这一点。在那儿? 这是一个例子:
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main(int argc, char** argv)
{
if(argc != 2)
{
std::exit(1);
}
std::string str = argv[1];
auto iter = str.begin();
bool r = qi::phrase_parse(iter, str.end(), qi::char_ >> qi::char_, qi::blank);
if (r && iter == str.end())
{
std::cout << "parse succeeded\n";
}
else
{
std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
}
}
这允许 ab
以及 a b
。我只想允许后者。
与此相关:跳过解析器究竟是如何工作的?一个提供类似 qi::blank 的东西,那么 kleene star 是否应用于形成跳过解析器?我想在这里得到一些启示,也许这也有助于解决这个问题。
附加信息:我的真实解析器看起来像这样:
one = char_("X") >> repeat(2)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
two = char_("Y") >> repeat(3)[omit[+blank] >> +alnum];
three = char_("Z") >> repeat(4)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
main = one | two | three;
这使得语法非常嘈杂,我想避免这种情况。
首先,我通常在(总是?)RFC 中看到这种要求的语法规范。在 99% 的情况下没有问题,考虑例如:
myrule = skip(space) [ uint_ >> uint_ ];
这已经隐含地要求数字之间至少有 1 个空白字符,原因很简单,否则会有 1 个数字。同样的简化发生在很多情况下令人惊讶(例如,参见上周这个答案中围绕无处不在的 WSP 产品所做的简化
除此之外,根据定义,skippers 应用零次或多次,因此没有办法通过 skip()
等现有状态指令获得您想要的内容。另请参阅 or the docs - 在 lexeme
、[no_]skip
和 skip_flag::dont_postskip
下)。
看你的具体语法,我会这样做:
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
在这里,您可以在词位内添加一个否定的先行断言来断言 "the end of the token was reached" - 在您的解析器中将被强制为 !qi::graph
:
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
观看演示:
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::string(iter, str.end());
}
std::cout << std::endl;
}
}
版画
--- "ab" --- parse failed. Remaining unparsed: ab
--- " ab " --- parse failed. Remaining unparsed: ab
--- " a b " --- parse succeeded.
--- "a b" --- parse succeeded.
奖金审查笔记
我的指导方针是:
- 你的船长应该是语法的负责人。令人遗憾的是,所有 Qi 样本都让人们相信您需要让调用者决定
- 结束迭代器检查不等于错误检查。很有可能parse things correctly without consuming all input。这就是为什么报告 "remaining input" 不应该只发生在解析失败的情况下。
- 如果尾随未解析的输入 是 错误,spell it out:
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
for (std::string const str : { "ab", " ab ", " a b ", "a b happy trees are trailing" }) {
auto iter = str.begin(), end = str.end();
auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);
bool r = qi::parse(iter, end, qi::skip(qi::space) [ token >> token >> qi::eoi ]);
std::cout << " --- " << std::quoted(str) << " --- ";
if (r) {
std::cout << "parse succeeded.";
} else {
std::cout << "parse failed.";
}
if (iter != end) {
std::cout << " Remaining unparsed: " << std::quoted(std::string(iter, str.end()));
}
std::cout << std::endl;
}
}
版画
--- "ab" --- parse failed. Remaining unparsed: "ab"
--- " ab " --- parse failed. Remaining unparsed: " ab "
--- " a b " --- parse succeeded.
--- "a b happy trees are trailing" --- parse failed. Remaining unparsed: "a b happy trees are trailing"