Boost.x3: атрибут накапливается между альтернативами

У меня есть парсер для синтаксического анализа идентификатора, такого как foo, bar, baz, и один для синтаксического анализа также вложенных идентификаторов, таких как foo::bar, foo::bar.baz, foo::bar.baz.baham. Они оба анализируют одну и ту же структуру ast, которая выглядит так:

struct identifier : x3::position_tagged{
    std::vector <std::string> namespaces;
    std::vector <std::string> classes;
    std::string identifier;

};

Парсер для identifier выглядит так:

#define VEC_ATR x3::attr(std::vector<std::string>({})) //ugly hack

auto const identifier_def =
                VEC_ATR
                >> VEC_ATR
                >> id_string;

а для nested_identifier вот так:

auto const nested_identifier_def =
        x3::lexeme[
                (+(id_string >> "::") >> +(id_string >> ".") > id_string)
                | (+(id_string >> "::") >> VEC_ATR > id_string)
                | (VEC_ATR >> +(id_string >> ".") > id_string)
                | identifier

        ];

Я знаю, что мне стыдно за макрос. Парсер идентификатора работает нормально, но nested_identifier ведет себя странно, если я пытаюсь проанализировать что-то вроде foo::bar::baz, объекты ast, которые выпадают из парсера, имеют все пространства имен, в данном случае foo и bar дважды в namespaces векторе. У меня есть небольшой пример этого странного поведения здесь. Кто-нибудь может объяснить мне, почему это происходит и как этого избежать?


person Exagon    schedule 16.09.2016    source источник
comment
что делает его полезным отличать классы от пространств имен? Например. в C ++ классы являются пространствами имен.   -  person sehe    schedule 17.09.2016
comment
Вы правы, я хотел, чтобы астрономы содержали как можно больше информации. Вот почему я анализирую идентификатор, за которым следует точка в векторе классов. возможно, классы - не лучшее название, но я не мог найти другого быстрого   -  person Exagon    schedule 17.09.2016


Ответы (2)


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

В вашем случае происходит вот что:

  • Первоначально атрибут [{},{},""].
  • Пробуется первая альтернативная ветка.
  • id_string >> "::" совпадает дважды и добавляет foo и bar к первому вектору -> _ 5_.
  • id_string >> "." не соответствует -> последовательность не выполняется -> альтернативная ветвь не выполняется (атрибут остается неизменным).
  • Пробуется вторая альтернативная ветка.
  • id_string >> "::" совпадает дважды и добавляет foo и bar к первому вектору -> _ 10_.
  • attr(vector<string>({})) успешно (attr всегда успешно) и заменяет пустой второй вектор вектором с пустой строкой -> [{foo,bar,foo,bar},{""},""].
  • id_string совпадает и baz добавляется к атрибуту -> _ 16_.
  • Вторая альтернативная ветвь успешна.

В Spirit.Qi решение в этом случае довольно просто, просто используйте директива hold. К сожалению, эта директива еще не реализована в Spirit.X3. Возможной альтернативой может быть размещение каждой из альтернативных ветвей в ее собственном x3::rule либо явно, либо с as<ast::identifier>(alternative_branch), как использовано здесь sehe. Вот упрощенный пример, демонстрирующий подход as.

Другой возможностью может быть реализация директивы hold, вот моя попытка (работает на WandBox):

#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>

namespace boost { namespace spirit { namespace x3
{
    template <typename Subject>
    struct hold_directive : unary_parser<Subject, hold_directive<Subject>>
    {
        typedef unary_parser<Subject, hold_directive<Subject> > base_type;
        static bool const is_pass_through_unary = true;
        static bool const handles_container = Subject::handles_container;

        hold_directive(Subject const& subject)
          : base_type(subject) {}

        template <typename Iterator, typename Context
          , typename RContext, typename Attribute>
        bool parse(Iterator& first, Iterator const& last
          , Context const& context, RContext& rcontext, Attribute& attr) const
        {
            Attribute copy(attr);
            if (this->subject.parse(first, last, context, rcontext, copy))
            {
                traits::move_to(copy, attr);
                return true;
            }
            return false;
        }

    };

    struct hold_gen
    {
        template <typename Subject>
        hold_directive<typename extension::as_parser<Subject>::value_type>
        operator[](Subject const& subject) const
        {
            return { as_parser(subject) };
        }
    };

    auto const hold = hold_gen{};
}}}
person Community    schedule 16.09.2016
comment
никогда раньше не пользовался этим as<something>. как это работает? Большое спасибо за вашу помощь, я не ожидал такого поведения. - person Exagon; 17.09.2016
comment
Я думаю, это ссылка на место, где это объяснено :) - person sehe; 17.09.2016
comment
о, извини, я был слишком голоден - person Exagon; 17.09.2016
comment
@Exagon Здесь упрощенный пример, демонстрирующий подход as. - person llonesmiz; 17.09.2016
comment
@sehe и jv_ Я бы облажался без вас двоих, спасибо, что вы все время помогали мне духом - person Exagon; 17.09.2016
comment
@sehe, почему в x3 не добавлены парсеры для работы и удержания? .... - person rmawatson; 11.07.2018
comment
@rmawatson Я их не предлагал. Думаю, если бы они захотели, они бы добавили их в низкоуровневом стиле. Для меня все дело в том, что вам не нужна специальная поддержка библиотеки для них, поскольку они довольно легко выражаются в C ++ 14. - person sehe; 11.07.2018
comment
@rmawatson Еще одно свидетельство универсальности C ++ 14 с X3: перенос подмножества Phoenix для семантических действий в X3 - person sehe; 11.07.2018
comment
@sehe будет действительно здорово видеть их слияния. Позор, что такие полезные биты плавают на SO. - person rmawatson; 12.07.2018
comment
Я за библиотеки с поддерживаемым документированным интерфейсом. Выбор правильного основного набора для обслуживания - непростая задача. - person sehe; 12.07.2018

Обратите внимание, что с Boost1.70 решение, предложенное @sehe, больше не работает (см. this обсуждение).

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

person Igor R.    schedule 25.08.2019