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