Boost.Spirit X3 Альтернативный оператор - PullRequest
0 голосов
/ 22 мая 2018

У меня есть следующий код:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

struct printer {
    template <typename int_type>
    void operator()(std::vector<int_type> &vec) {
        std::cout << "vec(" << sizeof(int_type) << "): { ";
        for( auto const &elem : vec ){
            std::cout << elem << ", ";
        }
        std::cout << "}\n";
    }
};

template <typename Iterator>
void parse_int_list(Iterator first, Iterator last) {
    namespace x3 = boost::spirit::x3;
    x3::variant<vector<uint32_t>, vector<uint64_t>> vecs;
    x3::parse( first, last,
            (x3::uint32 % '|') | (x3::uint64 % '|'), vecs );
    boost::apply_visitor(printer{}, vecs);
}

Я ожидал, что сначала попытается выполнить синтаксический анализ входных данных в 32-битном векторе uint, а затем в случае сбоя в 64-битном векторе uint.Это прекрасно работает, если первое целое число в списке соответствует типу, который достаточно велик для чего-либо еще в списке.Т.е.,

string ints32 = "1|2|3";
parse_int_list(being(ints32), end(ints32))
// prints vec(4): { 1, 2, 3, }

string ints64 = "10000000000|20000000000|30000000000";
parse_int_list(being(ints64), end(ints64))
// prints vec(8): { 10000000000, 20000000000, 30000000000, }

Однако это не работает, когда первое число является 32-разрядным, а позднее - 64-разрядным.

string ints_mixed = "1|20000000000|30000000000";
parse_int_list(being(ints_mixed), end(ints_mixed))
// prints vec(4): { 1, }

Возвращаемое значение x3::parse указываетпарсинг сбой .Но согласно моему прочтению документации следует попробовать второй вариант, если он не может проанализировать первый.

Любые указатели на то, как я читаю это неправильно, и какальтернативный парсер на самом деле работает?

Редактировать : После просмотра ответов я понял, что x3::parse фактически возвращает анализ success .Я проверял, что он проанализировал весь поток, first == last, чтобы определить успех, как показано в документации.Тем не менее, это скрывает тот факт, что благодаря жадной природе чистой звезды и отсутствию привязки к концу потока, она была в состоянии успешно проанализировать часть входных данных.Спасибо всем.

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Как прокомментировал Фрэнк, проблема в том, что оператор списка Клини был жадным, принимая столько элементов, сколько будет соответствовать, и считая, что это «совпадение».

Если вы хотите, чтобы он отклонял ввод if, некоторые элементыне были проанализированы ", сделайте так:

parse(first, last, x3::uint32 % '|' >> x3::eoi | x3::uint64 % '|' >> x3::eoi, vecs);

Демо

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>

struct printer {
    template <typename int_type> void operator()(std::vector<int_type> &vec) const {
        std::cout << "vec(" << sizeof(int_type) << "): { ";
        for (auto const &elem : vec) {
            std::cout << elem << ", ";
        }
        std::cout << "}\n";
    }
};

template <typename Iterator> void parse_int_list(Iterator first, Iterator last) {
    namespace x3 = boost::spirit::x3;
    boost::variant<std::vector<uint32_t>, std::vector<uint64_t> > vecs;

    parse(first, last, x3::uint32 % '|' >> x3::eoi | x3::uint64 % '|' >> x3::eoi, vecs);
    apply_visitor(printer{}, vecs);
}

int main() {
    for (std::string const input : {
             "1|2|3",
             "4294967295",
             "4294967296",
             "4294967295|4294967296",
         }) {
        parse_int_list(input.begin(), input.end());
    }
}

Отпечатки

vec(4): { 1, 2, 3, }
vec(4): { 4294967295, }
vec(8): { 4294967296, }
vec(8): { 4294967295, 4294967296, }
0 голосов
/ 22 мая 2018

Проблема здесь в том, что «3» является допустимым входным значением для парсера (x3::uint32 % '|'), поэтому первая ветвь альтернативного прохода, потребляющая только 3.

Самый чистый способ исправить этобудет иметь список альтернатив вместо списка списков.

т.е.:

(x3::uint32 | x3::uint64) % '|'

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

vector<x3::variant<uint32_t,uint64_t>> vecs;

Редактировать :

В качестве альтернативы, если вы не собираетесь использовать этот анализатор в качестве подпарасера, вы можете принудительно завершить ввод в каждой ветви.

(x3::uint32 % '|' >> x3::eoi) | (x3::uint64 % '|' >> x3::eoi)

Это приведет к сбою первой ветви, если она не достигнет конца потока, а затем перейдет к альтернативе.

...