我一直在尝试使用Qi解析一个简单的新行分隔符 顶点文件。采用以下格式(用我简单易懂的符号表示):
double double double optional(either (int int int optional(int)) or (double double double optional(double)))
我的测试用例开始失败repeat
,我找不到错误。希望代码中的注释更具启发性:
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
qi::rule<std::string::iterator, ascii::space_type> vertexRule =
(double_ >> double_ >> double_);
qi::rule<std::string::iterator, ascii::space_type> colorRule =
(double_ >> double_ >> double_ >> -(double_)) | (uint_ >> uint_ >> uint_ >> -(uint_));
template<typename Iterator, typename Rule>
bool parseIt(Iterator begin, Iterator end, Rule rule) {
bool r = qi::phrase_parse(
begin, end,
rule,
ascii::space
);
if(begin != end) {
std::cout << "No full match!" << std::endl;
while(begin != end)
std::cout << *begin++;
return false;
}
return r;
}
int main()
{
qi::rule<std::string::iterator, ascii::space_type> rule1 =
repeat(3)[vertexRule >> -(colorRule)];
std::string t1{
"20.0 20.0 20.0\n"
"1.0 1.0 1.0 255 0 255 23\n"
"1.0 1.0 1.0 1.0 0.3 0.2 0.3\n"
};
std::cout << std::boolalpha;
// matches
std::cout << parseIt(t1.begin(), t1.end(), rule1) << std::endl;
// 3 double 3 ints
std::string test{"1.0 1.0 1.0 1 3 2\n"};
// matches individually
std::cout << parseIt(test.begin(), test.end(), vertexRule >> -(colorRule)) << std::endl;
// offending line added at the end
// but position does not matter
// also offending 3 double 3 double
std::string t2{
"20.0 20.0 20.0\n"
"1.0 1.0 1.0 255 0 255 23\n"
"1.0 1.0 1.0 1.0 0.3 0.2 0.3\n"
"1.0 1.0 1.0 1 3 2\n"
};
qi::rule<std::string::iterator, ascii::space_type> rule2 =
repeat(4)[vertexRule >> -(colorRule)];
// does not match
std::cout << parseIt(t2.begin(), t2.end(), rule2) << std::endl;
// interestingly this matches
// std::string t2{
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// };
}
我是解析器构建的新手,尤其是Boost.Spirit。因此,赞赏指出明显的评论也很受欢迎。
答案 0 :(得分:4)
您的散文描述和示例输入似乎表明行尾对您的语法有重要意义。
然而,我找不到任何证据证明你试图在你的规则中表达这一点。
double_
和uint_
之间存在歧义还有另外一个问题(见下文)。
这是一个重新设计的示例,它添加了一个自定义的队长(不会吃 eol
)。此外,我让它接受任意数量的尾随eol
,但没有别的:
skipper = qi::char_(" \t");
bool r = qi::phrase_parse(
begin, end,
(vertexRule >> -colorRule) % qi::eol >> *qi::eol >> qi::eoi,
skipper
);
所有解析的完整代码返回成功:
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
template<typename Iterator>
bool parseIt(Iterator begin, Iterator end)
{
qi::rule<Iterator, qi::blank_type> vertexRule, colorRule;
vertexRule = double_ >> double_ >> double_;
colorRule = (double_ >> double_ >> double_ >> -(double_)) | (uint_ >> uint_ >> uint_ >> -(uint_));
bool r = qi::phrase_parse(
begin, end,
(vertexRule >> -colorRule) % qi::eol >> *qi::eol >> qi::eoi,
qi::blank
);
if(begin != end)
{
std::cout << "No full match! '" << std::string(begin, end) << std::endl;
return false;
}
return r;
}
int main()
{
std::string t1
{
"20.0 20.0 20.0\n"
"1.0 1.0 1.0 255 0 255 23\n"
"1.0 1.0 1.0 1.0 0.3 0.2 0.3\n"
};
std::cout << std::boolalpha;
// matches
std::cout << parseIt(t1.begin(), t1.end()) << std::endl;
// 3 double 3 ints
std::string test {"1.0 1.0 1.0 1 3 2\n"};
// matches individually
std::cout << parseIt(test.begin(), test.end()) << std::endl;
// offending line added at the end
// but position does not matter
// also offending 3 double 3 double
std::string t2
{
"20.0 20.0 20.0\n"
"1.0 1.0 1.0 255 0 255 23\n"
"1.0 1.0 1.0 1.0 0.3 0.2 0.3\n"
"1.0 1.0 1.0 1 3 2\n"
};
// does not match
std::cout << parseIt(t2.begin(), t2.end()) << std::endl;
// interestingly this matches
// std::string t2{
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// "1.0 1.0 1.0 1 3 2\n"
// };
}
uint_
与double_
如上所述,这里也存在一种含糊不清的含糊之处:
colorRule = (double_ >> double_ >> double_ >> -(double_)) | (uint_ >> uint_ >> uint_ >> -(uint_));
目前,规则的(uint_ >> uint_ >> uint_ >> -(uint_)
部分永远不会匹配,因为它也会匹配第一部分(double_
)。我只是将其重写为
colorRule = double_ >> double_ >> double_ >> -double_;
除非值被指定为浮动值,否则值的含义会发生变化(例如,uints从0..255开始,但是双倍从0.0..1.0开始)。在那种情况下,我可以看到为什么你想要检测整数。您可以通过重新排序来实现这一目标。
colorRule = (uint_ >> uint_ >> uint_ >> -(uint_))
| (double_ >> double_ >> double_ >> -(double_));
为了让解析器的用户更容易,我只是在任何时候都公开相同的属性类型,并考虑使用任何适当的转换将整数转换为双精度的语义操作:
#include <boost/spirit/include/phoenix_operator.hpp>
// ....
qi::rule<Iterator, Skipper, double()> colorInt = uint_ [ _val = _1 / 255.0 ];
colorRule = (colorInt >> colorInt >> colorInt >> -(colorInt))
| (double_ >> double_ >> double_ >> -(double_));