Проблемы с boost::spirit::lex и пробелами

Я пытаюсь научиться использовать boost::spirit. Для этого я хотел создать какой-нибудь простой лексер, объединить их, а затем начать парсить с помощью Spirit. Я попытался изменить пример, но он не работает должным образом (результат r неверен).

Вот лексер:

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

namespace lex = boost::spirit::lex;

template <typename Lexer>
struct lexer_identifier : lex::lexer<Lexer>
{
    lexer_identifier()
        : identifier("[a-zA-Z_][a-zA-Z0-9_]*")
        , white_space("[ \\t\\n]+")
    {
        using boost::spirit::lex::_start;
        using boost::spirit::lex::_end;

        this->self = identifier;
        this->self("WS") = white_space;
    }
    lex::token_def<> identifier;
    lex::token_def<> white_space;
    std::string identifier_name;
};

И это пример, который я пытаюсь запустить:

#include "stdafx.h"

#include <boost/spirit/include/lex_lexertl.hpp>
#include "my_Lexer.h"

namespace lex = boost::spirit::lex;

int _tmain(int argc, _TCHAR* argv[])
{
    typedef lex::lexertl::token<char const*,lex::omit, boost::mpl::false_> token_type;
    typedef lex::lexertl::lexer<token_type> lexer_type;

    typedef lexer_identifier<lexer_type>::iterator_type iterator_type;

    lexer_identifier<lexer_type> my_lexer;

    std::string test("adedvied das934adf dfklj_03245");

    char const* first = test.c_str();
    char const* last = &first[test.size()];

    lexer_type::iterator_type iter = my_lexer.begin(first, last);
    lexer_type::iterator_type end = my_lexer.end();

    while (iter != end && token_is_valid(*iter))
    {
        ++iter;
    }

    bool r = (iter == end);

    return 0;
}

r истинно, пока в строке есть только один токен. Почему это так?

С уважением Тобиас


person Tobias Langner    schedule 13.11.2012    source источник


Ответы (1)


Вы создали второе состояние лексера, но так и не вызвали его.

Упрощайте и получайте прибыль:


В большинстве случаев самый простой способ добиться желаемого эффекта — использовать лексирование с одним состоянием с флагом pass_ignore для пропускаемых токенов:

    this->self += identifier
                | white_space [ lex::_pass = lex::pass_flags::pass_ignore ];

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

typedef lex::lexertl::actor_lexer<token_type> lexer_type;

Полный образец:

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
namespace lex = boost::spirit::lex;

template <typename Lexer>
struct lexer_identifier : lex::lexer<Lexer>
{
    lexer_identifier()
        : identifier("[a-zA-Z_][a-zA-Z0-9_]*")
        , white_space("[ \\t\\n]+")
    {
        using boost::spirit::lex::_start;
        using boost::spirit::lex::_end;

        this->self += identifier
                    | white_space [ lex::_pass = lex::pass_flags::pass_ignore ];
    }
    lex::token_def<> identifier;
    lex::token_def<> white_space;
    std::string identifier_name;
};

int main(int argc, const char *argv[])
{
    typedef lex::lexertl::token<char const*,lex::omit, boost::mpl::false_> token_type;
    typedef lex::lexertl::actor_lexer<token_type> lexer_type;

    typedef lexer_identifier<lexer_type>::iterator_type iterator_type;

    lexer_identifier<lexer_type> my_lexer;

    std::string test("adedvied das934adf dfklj_03245");

    char const* first = test.c_str();
    char const* last = &first[test.size()];

    lexer_type::iterator_type iter = my_lexer.begin(first, last);
    lexer_type::iterator_type end = my_lexer.end();

    while (iter != end && token_is_valid(*iter))
    {
        ++iter;
    }

    bool r = (iter == end);
    std::cout << std::boolalpha << r << "\n";
}

Отпечатки

true

"WS" как состояние шкипера


Также возможно, что вы столкнулись с образцом, в котором используется второе состояние синтаксического анализатора для шкипера (lex::tokenize_and_phrase_parse). Позвольте мне потратить минуту или 10, чтобы создать рабочий образец для этого.

Обновление Заняло у меня чуть больше 10 минут (уааааа) :) Вот сравнительный тест, показывающий, как взаимодействуют состояния лексера и как использовать синтаксический анализ Spirit Skipper для вызова второго состояния синтаксического анализатора:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
namespace lex = boost::spirit::lex;
namespace qi  = boost::spirit::qi;

template <typename Lexer>
struct lexer_identifier : lex::lexer<Lexer>
{
    lexer_identifier()
        : identifier("[a-zA-Z_][a-zA-Z0-9_]*")
        , white_space("[ \\t\\n]+")
    {
        this->self       = identifier;
        this->self("WS") = white_space;
    }
    lex::token_def<> identifier;
    lex::token_def<lex::omit> white_space;
};

int main()
{
    typedef lex::lexertl::token<char const*, lex::omit, boost::mpl::true_> token_type;
    typedef lex::lexertl::lexer<token_type> lexer_type;

    typedef lexer_identifier<lexer_type>::iterator_type iterator_type;

    lexer_identifier<lexer_type> my_lexer;

    std::string test("adedvied das934adf dfklj_03245");

    {
        char const* first = test.c_str();
        char const* last = &first[test.size()];

        // cannot lex in just default WS state:
        bool ok = lex::tokenize(first, last, my_lexer, "WS");
        std::cout << "Starting state WS:\t" << std::boolalpha << ok << "\n";
    }

    {
        char const* first = test.c_str();
        char const* last = &first[test.size()];

        // cannot lex in just default state either:
        bool ok = lex::tokenize(first, last, my_lexer, "INITIAL");
        std::cout << "Starting state INITIAL:\t" << std::boolalpha << ok << "\n";
    }

    {
        char const* first = test.c_str();
        char const* last = &first[test.size()];

        bool ok = lex::tokenize_and_phrase_parse(first, last, my_lexer, *my_lexer.self, qi::in_state("WS")[my_lexer.self]);
        ok = ok && (first == last); // verify full input consumed
        std::cout << std::boolalpha << ok << "\n";
    }
}

Выход

Starting state WS:  false
Starting state INITIAL: false
true
person sehe    schedule 13.11.2012
comment
Добавлен подход состояния WS с демонстрацией в разделе "WS" as a Skipper state. Ваше здоровье - person sehe; 14.11.2012
comment
Упс. Я скопировал неправильное объявление token_type. Требуется mpl::true_ для HasState при работе с лексерами с отслеживанием состояния — очевидно! Исправлено - person sehe; 14.11.2012
comment
прежде всего - спасибо за ваш обширный пример. У меня все еще есть несколько вопросов: что делает lex::omit? Что касается вызова tokenize_and_parse: что такое my_lexer.self и qi::in_state(WS)[my_lexer.self]? - person Tobias Langner; 14.11.2012
comment
my_lexer.self — это все токены для состояния лексера по умолчанию (INITIAL), а in_state("WS")[my_lexer.self] — все токены для состояния лексера WS. Они были определены вами. Первое выражение передается как выражение синтаксического анализатора (просто: соответствует любому количеству токенов), а второе передается как шкипер (просто: пропускает любые пробелы). - person sehe; 14.11.2012
comment
еще раз спасибо. Просто для моего понимания - если я назову это-›self(ID_BLA) = bla_token; затем он добавит новое состояние лексера с именем ID_BLA? - person Tobias Langner; 14.11.2012
comment
@TobiasLangner Действительно. Оказывается, документация по Лексу немного тонковата, я полагаю, я нашел ее в образце (?) - person sehe; 14.11.2012