Boost 与qi::repeat和可选解析器不匹配

Boost 与qi::repeat和可选解析器不匹配,boost,boost-spirit,Boost,Boost Spirit,我一直在尝试使用Qi来解析一个简单的新行分隔符 顶点文件。采用以下格式(以我自己制作的易于阅读的符号表示): double可选(int可选(int))或(double可选(double)) 我的测试用例开始失败,重复,我找不到错误。代码中的注释希望更具启发性: #include <boost/spirit/include/qi.hpp> #include <string> #include <iostream> using namespace boost::

我一直在尝试使用Qi来解析一个简单的新行分隔符 顶点文件。采用以下格式(以我自己制作的易于阅读的符号表示):

double可选(int可选(int))或(double可选(double))

我的测试用例开始失败,重复
,我找不到错误。代码中的注释希望更具启发性:

#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;
qi::规则顶点规则=
(双倍>>双倍>>双倍);
qi::规则颜色规则=
(双双)(双)(双)(双)(双)(双))(双;
模板
bool parseIt(迭代器开始、迭代器结束、规则){
bool r=qi::短语解析(
开始,结束,
规则,
ascii::空格
);
如果(开始!=结束){
std::cout你的散文描述和示例输入似乎表明行尾对你的语法有意义

然而,我找不到任何证据证明你试图在规则中表达这一点

另一个问题是
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\u>>uint\u>>uint\u>>-(uint\u)
部分永远不会匹配,因为它也会匹配第一部分(使用
双精度

colorRule  = double_ >> double_ >> double_ >> -double_;
当然,除非将值指定为浮点数(例如,uint从0..255变为,而double从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_));
#包括
// ....
qi::规则colorInt=uint_[\u val=\u 1/255.0];
colorRule=(colorInt>>colorInt>>colorInt>>-(colorInt))
|(双双)(双)(双)(双)(双));

我的印象是,新行是自动处理的,因为它在大多数运行中似乎都是有效的。显然我错了。对我来说,跳过仍然是最黑暗的精神领域之一。@pmr:跳过实际上太简单了;大多数人希望它能发挥神奇的作用,但事实并非如此:跳过者只会跳过所有跳过的内容(通常是空白)在解析器看到输入之前。查看
qi::lexeme[]
(要禁用表达式的跳过),
qi::raw[]
(要返回原始源迭代器,包括跳过的字符),
qi::distinct
(来自Spirit存储库——要求在解析器边界处跳过)管理跳过程序。请注意,对于用户,例如
qi::lexeme[]
内部的
colorInt
,您必须从规则类型中删除跳过程序:)还有一件事:远离跳过程序意味着:在规则中提及每个跳过的标记,并使用
qi::parse
而不是
qi::parse\u短语
#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_));