Почему Boost.Spirit корректно разбирает идентификатор в std::string, а не в адаптированную структуру, состоящую исключительно из std::string?

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

Если атрибут моей грамматики равен std::string, Qi правильно адаптирует к нему последовательность символов. Но в структуре сохраняется только первый символ. Я не совсем уверен, почему это так. (Обратите внимание, что не имеет значения, является ли структура «действительно» адаптированной или она была определена встроенным Fusion.)

Вот SSCCE, настраиваемый для отладки:

// Options:
//#define DEFINE_STRUCT_INLINE
//#define DEBUG_RULE

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/fusion/adapted/struct/define_struct_inline.hpp>
#include <boost/fusion/include/define_struct_inline.hpp>

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <iostream>
#include <string>

namespace qi = boost::spirit::qi;

#ifdef DEFINE_STRUCT_INLINE
    namespace example
    {
        BOOST_FUSION_DEFINE_STRUCT_INLINE(
            identifier_result,
            (std::string, name)
            )
    }
#else
    namespace example
    {
        struct identifier_result
        {
            std::string name;
        };
    }

    BOOST_FUSION_ADAPT_STRUCT(
        example::identifier_result,
        (std::string, name)
        )
#endif

namespace example
{
    typedef std::string identifier_result_str;

    template <typename Iterator, typename Result>
    struct identifier_parser : qi::grammar<Iterator, Result()>
    {
        identifier_parser() :
        identifier_parser::base_type(identifier, "identifier_parser")
        {
            identifier %=
                qi::alpha >>
                *qi::alnum
                ;

            identifier.name("identifier");

            #ifdef DEBUG_RULE
                debug(identifier);
            #endif
        }

        qi::rule<Iterator, Result()> identifier;
    };
}

std::string strip(example::identifier_result identifier)
{
    return identifier.name;
}

std::string strip(std::string str)
{
    return str;
}

template <typename Result>
void test_parse(const std::string& input)
{
    using namespace example;

    auto&& first = input.cbegin();
    auto&& last = input.cend();

    auto&& parser = identifier_parser<std::string::const_iterator, Result>();
    auto&& skipper = qi::space;

    Result result;
    qi::phrase_parse(first, last, parser, skipper, result);

    std::cout << "Result of the parse is: \'"
              << strip(result) << "\'" << std::endl;
}

int main()
{
    using namespace example;

    test_parse<identifier_result>(" validId1 ");
    test_parse<identifier_result>(" %error1% ");

    test_parse<identifier_result_str>(" validId2 ");
    test_parse<identifier_result_str>(" %error2% ");
}

Результат:

Результат синтаксического анализа: 'v'
Результат синтаксического анализа: ''
Результат синтаксического анализа: 'validId2'
Результат синтаксического анализа: ''

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

Если я отлаживаю узел, я получаю этот вывод:

<identifier>
  <try>validId1 </try>
  <success> </success>
  <attributes>[[[v]]]</attributes>
</identifier>

[ ... ]

<identifier>
  <try>validId2 </try>
  <success> </success>
  <attributes>[[v, a, l, i, d, I, d, 2]]</attributes>
</identifier>

Итак, я вижу, что правило использует весь идентификатор, просто неправильно его сохраняет. Единственный «намек», который у меня есть на разницу, заключается в том, что v в первом случае вложено в [[[.]]], а правильный случай - только [[.]]. Но я не знаю, что с этим делать. :)

Почему происходит такое поведение?


person GManNickG    schedule 10.08.2013    source источник
comment
У меня была такая же проблема с Spirit X3. Добавление промежуточной дополнительной оболочки правила решило проблему.   -  person Xeverous    schedule 06.01.2019


Ответы (1)


Просто чтобы начать работу, вы должны обернуть свою строку дополнительным правилом.

Я не знаю точного объяснения, но то, что вы хотите сделать, это проанализировать строку последовательностью парсеров char. С string в качестве типа атрибута qi может использовать атрибут как контейнер для хранения нескольких символов, со структурой он просто не знает, как это сделать. Возможно, это помогло бы задать свойства контейнера структуры, но у меня нет опыта в этом. И просто для синтаксического анализа строки, что может быть излишним.

Здесь помогает просто изменение вашего парсера:

namespace example
{
    typedef std::string identifier_result_str;

    template <typename Iterator, typename Result>
    struct identifier_parser : qi::grammar<Iterator, Result()>
    {
        identifier_parser() :
        identifier_parser::base_type(identifier, "identifier_parser")
        {
            string %=
                qi::alpha >>
                *qi::alnum
                ;

            identifier = string;
            identifier.name("identifier");

            #ifdef DEBUG_RULE
                debug(identifier);
            #endif
        }

        qi::rule<Iterator, Result()> identifier;
        qi::rule<Iterator, std::string()> string;
    };
}
person Mike M    schedule 10.08.2013
comment
Да, я тоже придумал этот обходной путь. Хорошо, что я перечислил здесь, но мне все еще любопытно, зачем нужна эта косвенность. Я не вижу способа заполнить значение структуры, не пройдя сначала std::string. Я дам этому +1, как только появится полный ответ, но пока это только дополнительная информация. - person GManNickG; 11.08.2013
comment
Как уже говорилось, вы анализируете последовательность символов, а не строку в духовном смысле, поэтому ваш атрибут должен иметь свойства контейнера. - person Mike M; 11.08.2013
comment
Извините, я не куплюсь на это. Атрибут должен быть tuple<char, vector<char>>, который можно преобразовать в vector<char>, который можно преобразовать в string. Как Ци попадает из tuple<char, vector<char>> в мою структуру, минуя string? Он не будет (или не должен) просто отбрасывать вторую половину кортежа. Не то, чтобы вы обязательно ошибались (может быть, я просто туплю), но я ищу формальные причины, а не эвристики. - person GManNickG; 11.08.2013
comment
@GManNickG Этот комментарий основан не на знании базы кода духа, а на опыте и экспериментах. Так что я не могу быть на 100% уверен, что это правильно. tuple<char,vector<char>> можно преобразовать в vector<char>, если передаваемый атрибут является контейнером. Атрибут, который вы передаете здесь, в основном tuple<vector<char>>. Еще одна проблема со Spirit заключается в том, что он без проблем назначает кортеж более короткому, отбрасывая все элементы, превышающие длину последнего. Вы можете увидеть пример этого здесь. - person llonesmiz; 11.08.2013
comment
@GManNickG Я думаю, что обходной путь в этом ответе и так хорош, но вы также можете использовать identifier=qi::as_string[qi::alpha >> *qi::alnum];. - person llonesmiz; 11.08.2013
comment
@GManNickG Вы можете увидеть пример того, как задать свойства контейнера структуры здесь . Упрощенная версия кода, работающего на coliru. - person llonesmiz; 11.08.2013
comment
@cv_and_he: Спасибо за поддержку ответа. Я просто очень удивлен, что Qi допускает (для меня) потерю данных, но если это так, то это так. И поскольку это так, предпосылка, что она должна добраться до строки до того, как структура будет нарушена. Спасибо всем! - person GManNickG; 11.08.2013
comment
@GManNickG Это не по замыслу. Я помню, что некоторое время назад в списке рассылки была тема, в которой объяснялась проблема, но я не смог ее найти. - person llonesmiz; 11.08.2013