Разбор списка токенов с разделителями с использованием Boost Spirit Qi

Используя boost::spirit::qi, я пытаюсь разобрать строки, состоящие из метки, за которой следует переменное количество токенов с разделителями. Я вызываю грамматику с помощью phrase_parse и использую предоставленный синтаксический анализатор blank в качестве синтаксического анализатора пропуска, чтобы сохранить новые строки, поскольку мне нужно убедиться, что метка является первым элементом в каждой строке.

Простой базовый случай:

label token, token, token

Можно разобрать с помощью грамматики:

line = label >> (token % ',') >> eol;

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

label
label ,
label , token
label token, , token,

Мне не удалось создать грамматику, которая принимает все приведенные выше примеры. Любые предложения о том, как решить эту проблему?

Изменить:

Спасибо sehe за весь вклад в проблему, изложенную выше. Теперь самое интересное, что я забыл включить... Грамматика также должна принимать пустые и разделенные строки. (токены без метки) Когда я пытаюсь сделать метку необязательной, я получаю бесконечный цикл, соответствующий пустой строке.

label

label token
token

person Fredrik    schedule 24.10.2014    source источник
comment
Я предлагаю вам вместо этого использовать ANTLR. Я понимаю, что это не тот ответ, на который вы надеялись, поэтому я публикую его как комментарий. ANTLR намного проще в использовании в долгосрочной перспективе, у него лучшая поддержка инструментов и больше ссылок для обучения.   -  person John Zwinck    schedule 24.10.2014
comment
@JohnZwinck Мы понимаем, что тебе уже не нравится Spirit. В нем много чего не нравится (и в С++ вообще). Хорошо. Но предлагать использовать здесь ANTLR без какого-либо контекста, кроме поставленного вопроса, тоже немного нелепо. Звучит как канон, чтобы прихлопнуть муху.   -  person sehe    schedule 25.10.2014
comment
Это больше похоже на то, чтобы сказать кому-то, кто задает вопрос о русской грамматике, вместо этого переключиться на английский.   -  person Spacemoose    schedule 22.06.2015


Ответы (1)


Вы должны иметь возможность принять пустой список с помощью

line = label >> -(token % ',') >> eol;

Обратите внимание, что eol не будет работать, если ваш шкипер тоже пропускает eol (поэтому не используйте qi::space, а, например, qi::blank для этой цели)

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


В ответ на комментарий: полностью рабочий образец Live On Coliru

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

namespace qi = boost::spirit::qi;

int main()
{
    using namespace qi;

    using It     = std::string::const_iterator;
    using Token  = std::string;
    using Tokens = std::vector<Token>;

    rule<It, blank_type> label 
        = lexeme[+~char_(":")] >> ':'
        ;

    rule<It, Token(), blank_type> token
        = lexeme[*~char_(",\n")];
        ;

    rule<It, Tokens(), blank_type> line
        = label >> -(token % ',') >> eol
        ;

    for (std::string const input : {
        "my first label: 123, 234, 345 with spaces\n",
        "1:\n",
        "2: \n",
        "3: ,,,\n",
        "4: ,  \t ,,\n",
        "5: ,  \t , something something,\n",
    })
    {
        std::cout << std::string(40, '=') << "\nparsing: '" << input << "'\n";

        Tokens parsed;
        auto f = input.begin(), l = input.end();
        bool ok = phrase_parse(f, l, line, blank, parsed);

        if (ok)
        {
            std::cout << "Tokens parsed successfully, number parsed: " << parsed.size() << "\n";
            for (auto token : parsed)
                std::cout << "token value '" << token << "'\n";
        }
        else
            std::cout << "Parse failed\n";

        if (f != l)
            std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
    }
}

Выход:

========================================
parsing: 'my first label: 123, 234, 345 with spaces
'
Tokens parsed successfully, number parsed: 3
token value '123'
token value '234'
token value '345 with spaces'
========================================
parsing: '1:
'
Tokens parsed successfully, number parsed: 1
token value ''
========================================
parsing: '2: 
'
Tokens parsed successfully, number parsed: 1
token value ''
========================================
parsing: '3: ,,,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value ''
token value ''
========================================
parsing: '4: ,       ,,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value ''
token value ''
========================================
parsing: '5: ,       , something something,
'
Tokens parsed successfully, number parsed: 4
token value ''
token value ''
token value 'something something'
token value ''
person sehe    schedule 24.10.2014
comment
Когда я пытаюсь добавить правило для пустого токена, все ломается. Даже такое простое правило, как token = -lit("token"), не работает. Не могли бы вы привести пример? - person Fredrik; 30.10.2014
comment
@Fredrik Я добавил образец (я только что понял, что *(char_ - ',' - eol) будет немного точнее для токена, но вы можете решить, что :)) - person sehe; 30.10.2014
comment
На заметку: как отлаживать грамматики spirit::qi? Я видел некоторые комментарии об использовании определенных #define для активации отладочных аннотаций, но у меня это никогда не работало... - person Fredrik; 30.10.2014
comment
Ааа... на этот раз гугл-фу был со мной и я сразу нашел ответ. Просто позвоните debug(rule). - person Fredrik; 30.10.2014
comment
@Fredrik Я бы предложил гораздо более дружелюбные макросы BOOST_SPIRIT_DEBUG_NODE и BOOST_SPIRIT_DEBUG_NODES: BOOST_SPIRIT_DEBUG_NODES((label)(token)(line)) - person sehe; 30.10.2014
comment
Что касается правок, если у вас есть дополнительные вопросы, пожалуйста, задайте их на основном сайте. - person sehe; 30.10.2014