Boost Spirit QI: автоматическое удержание правила для кортежа с последовательностью внутри альтернативной функции

Я пытаюсь использовать автоматическое удержание для назначения в кортеж из трех элементов. Второй тип - это строка, а третий - вектор строки (тип первого параметра не так важен для этого примера).

Я пытаюсь понять, почему следующий код не компилируется.

#include <boost/fusion/include/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>

using namespace boost::spirit::qi;
using iterator = std::string::const_iterator;

void qi_compile_test1()
{
    rule<iterator, std::string(), space_type> r_s1, r_s2;
    rule<iterator, std::vector<std::string>(), space_type> r_vec_s1, r_vec_s2;
    rule<iterator, std::tuple<float, std::string, std::vector<std::string>>(), space_type> r;

    r %=
        float_ >
        ((r_s1 > r_vec_s1) |
         (r_s2 > r_vec_s2));
}

Ошибка заключается в следующем (функция insert вызывается для строки со строкой в ​​качестве второго параметра):

/opt/wandbox/boost-1.68.0/clang-6.0.1/include/boost/spirit/home/support/container.hpp:292:15: error: no matching member function for call to 'insert'
            c.insert(c.end(), val);
            ~~^~~~~~
/opt/wandbox/boost-1.68.0/clang-6.0.1/include/boost/spirit/home/support/container.hpp:354:51: note: in instantiation of member function 'boost::spirit::traits::push_back_container<std::__1::basic_string<char>, std::__1::basic_string<char>, void>::call' requested here
        return push_back_container<Container, T>::call(c, val);

Короче говоря, определено следующее правило:

A > ((B > C) | (B > C))

Я подозреваю, что ошибка происходит из-за того, что C совпадает с вектором , который может быть возобновлен как:

A > ((B > vector<B>) | (B > vector<B))

Основываясь на правилах составных атрибутов, я предполагаю, что ошибка возникает из следующего решения:

A > (vector<B> | vector<B>)     a : A, b: vector<A> --> (a > b): vector<A>
A > vector<B>                   a : A, b: A --> (a | b): A

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

A > ((B > vector<B>) | (B > vector<B))
A > (B > vector<B>)             a : A, b: A --> (a | b): A

Я не понимаю, что следующий код компилируется:

void qi_compile_test2()
{
    rule<iterator, int(), space_type> r_int1, r_int2;
    rule<iterator, std::vector<int>(), space_type> r_vec_int1, r_vec_int2;
    rule<iterator, std::tuple<float, int, std::vector<int>>(), space_type> r;

    r %=
        float_ >
        ((r_int1 > r_vec_int1) |
         (r_int2 > r_vec_int2));
}

Единственная разница заключается в замене типа B с std::string на int. Я предполагаю, что это упрощает вещи, потому что std::string является контейнером, но не типом 'int'. Я не вижу, что это изменение может вызвать в этом сценарии.

Кроме того, взяв первую формулу и удалив первый параметр кортежа, мы можем скомпилировать (сохраняя альтернативную функцию и порядок последовательности с включенной строкой).

void qi_compile_test3()
{
    rule<iterator, std::string(), space_type> r_s1, r_s2;
    rule<iterator, std::vector<std::string>(), space_type> r_vec_s1, r_vec_s2;
    rule<iterator, std::tuple<std::string, std::vector<std::string>>(), space_type> r;

    r %=
        ((r_s1 > r_vec_s1) |
         (r_s2 > r_vec_s2));
}

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

void qi_compile_test4()
{
    rule<iterator, std::tuple<std::string, std::vector<std::string>>(), space_type> r_p1, r_p2;
    rule<iterator, std::tuple<float, std::string, std::vector<std::string>>(), space_type> r;

    r %=
        float_ >
        (r_p1 | r_p2 );
}

Этот код можно скомпилировать здесь: https://wandbox.org/permlink/iJLRz1TKMK2pWMVb

Здесь я пытаюсь добиться того, чтобы последние два элемента кортежа зависели друг от друга. Я хочу сохранить последовательность двух элементов, но разделенных альтернативной функцией. Удаление альтернативной функции и сохранение только одного из двух операторов компилирует, но удаляет желаемую логику.

Я хочу избежать использования семантических действий, потому что в моем реальном сценарии случая мой кортеж содержит гораздо больше элементов (структура, перечисляемая слиянием), потому что, как только я использовал семантические действия, чтобы назначить правильное поле в кортеже, мне пришлось назначить каждому из них. У кого-нибудь есть предложения, как решить эту проблему?

Спасибо

1 ответ

К сожалению, Spirit (как Qi, так и X3) объединяет только кортежи анализаторов последовательностей1. Хотя кажется естественным ожидать плоского представления синтаксического анализатора, Дух не идет так глубоко. Он действует очень просто2, и исправление потребует огромного рефакторинга манипуляции атрибутами.

  1. Тем не менее, унарные прозрачны так float_ >> lexeme[int_ >> char_] иметь tuple<float, int, char> результат.
#include <boost/spirit/home/qi.hpp>
#include <boost/core/demangle.hpp>
#include <typeinfo>

namespace qi = boost::spirit::qi;

template <typename Iterator = char const*, typename Context = qi::unused_type, typename Parser>
void print_parser_attr_type(Parser const& p)
{
    using attr_type = typename decltype(boost::spirit::compile<qi::domain>(p))::template attribute<Context, Iterator>::type;
    std::cout << boost::core::demangle(typeid(attr_type).name()) << '\n';
}

int main()
{
    print_parser_attr_type(qi::float_ >> ((qi::int_ >> qi::char_) | (qi::int_ >> qi::char_)));
}

Выход:

boost::fusion::vector<float, boost::fusion::vector<int, char> >

  1. Вас может удивить, что float_ >> no_case[int_ >> char_] будет отлично разбираться в tuple<float, tuple<int, char>> но float_ >> (int_ >> char_) не буду.
Другие вопросы по тегам