Нет совпадения с qi :: repeat и необязательным парсером - PullRequest
2 голосов
/ 20 декабря 2011

Я экспериментировал с 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.Поэтому комментарии, указывающие на очевидное, также приветствуются.

1 Ответ

4 голосов
/ 20 декабря 2011

Ваше описание прозы и примеры вводных данных, кажется, указывают, что концы строк имеют значение для вашей грамматики.

Тем не менее, я не могу найти никаких доказательств того, что вы пытались выразить это в своих правилах.

Есть еще одна проблема с неопределенностью между 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_));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...