Как преобразовать значение токена boost::spirit::lex из iterator_range в строку?

Когда я пытаюсь преобразовать значение токена из iterator_range, лексер терпит неудачу при попытке прочитать следующий токен.

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

template <typename Lexer>
struct Tokens : boost::spirit::lex::lexer<Lexer>
{
    Tokens();

    boost::spirit::lex::token_def<std::string> identifier;
    boost::spirit::lex::token_def<std::string> string;
    boost::spirit::lex::token_def<bool> boolean;
    boost::spirit::lex::token_def<double> real;
    boost::spirit::lex::token_def<> comment;
    boost::spirit::lex::token_def<> whitespace;
};

template <typename Lexer>
Tokens<Lexer>::Tokens()
{
    // Define regex macros
    this->self.add_pattern
        ("LETTER", "[a-zA-Z_]")
        ("DIGIT", "[0-9]")
        ("INTEGER", "-?{DIGIT}+")
        ("FLOAT", "-?{DIGIT}*\\.{DIGIT}+");

    // Define the tokens' regular expressions
    identifier = "{LETTER}({LETTER}|{DIGIT})*";
    string = "\"[a-zA-Z_0-9]*\"";
    boolean = "true|false";
    real = "{INTEGER}|{FLOAT}";
    comment = "#[^\n\r\f\v]*$";
    whitespace = "\x20\n\r\f\v\t+";

    // Define tokens
    this->self
        = identifier
        | string
        | boolean
        | real
        | '{'
        | '}'
        | '<'
        | '>';

    // Define tokens to be ignored
    this->self("WS")
        = whitespace
        | comment;
}

Вот определение моих типов токенов и лексеров:

typedef lex::lexertl::token<char const*> TokenType;
typedef lex::lexertl::actor_lexer<TokenType> LexerType;

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

Tokens<LexerType> tokens;

std::string string = "9index";
char const* first = string.c_str();
char const* last = &first[string.size()];
LexerType::iterator_type token = tokens.begin(first, last);
LexerType::iterator_type end = tokens.end();

//typedef boost::iterator_range<char const*> iterator_range;
//const iterator_range& range = boost::get<iterator_range>(token->value());
//std::cout << std::string(range.begin(), range.end()) << std::endl;

++token;

token_is_valid(*token); // Returns false ONLY if I uncomment the above code

Вывод этого кода — «9» (он считывает первое число, оставляя «индекс» в потоке). Если я распечатаю значение строки (первый, последний) в этот момент, он покажет «ndex». По какой-то причине лексер не работает с этим символом «i»?

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

std::stringstream out;
out << token->value();
std::cout << out.str() << std::endl;

++token;

token_is_valid(*token); // still fails

Наконец, следующий токен действителен, если я просто отправлю значение токена в cout:

std::cout << token->value() << std::endl;

++token;

token_is_valid(*token); // success, what?

Что мне не хватает в том, как работает iterator_range, возвращаемый token->value()? Ни один из методов, которые я использовал для преобразования его в строку, похоже, не изменяет integer_range или входной поток символов лексера.

редактировать: я добавляю это здесь, так как ответ на комментарий слишком короткий, чтобы полностью объяснить, что произошло.

Я понял. Как указали sehe и drhirsch, код в моем первоначальном вопросе был стерилизованной версией того, что я на самом деле делаю. Я тестирую лексер, используя модульные тесты gtest с классом тестовых приспособлений. Как член этого класса у меня есть void scan(const std::string& str), который присваивает первый и последний итераторы (элементы данных фикстуры) из заданной строки. Проблема в том, что как только мы выходим из этой функции, параметр const std::string& str извлекается из стека и больше не существует, что делает эти итераторы недействительными, даже если они являются элементами данных фикстуры.

Мораль истории: объект, на который ссылаются итераторы, переданные в lexer::begin(), должен существовать до тех пор, пока вы ожидаете чтения токенов.

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


person May Oakes    schedule 02.05.2012    source источник
comment
какая версия компилятора/библиотеки? Необязательно флаги компилятора? Я исправил синтаксическую ошибку в вашем коде   -  person sehe    schedule 02.05.2012
comment
В следующий раз рассмотрите возможность публикации минимального автономного примера. Опытному разработчику Spirit требуется около 10 минут, чтобы настроить компилируемый пример из приведенных выше фрагментов. Это поднимает барьер для ответа примерно на «бесконечность» примерно для 95% аудитории SO.   -  person sehe    schedule 02.05.2012
comment
@Сехе Правда. И проблема может быть скрыта в той части кода, которую вы нам не показали   -  person Gunther Piez    schedule 02.05.2012
comment
Вы, ребята, делаете хорошее замечание, возможно, я сделаю это   -  person May Oakes    schedule 03.05.2012
comment
Спасибо, что оставили это здесь.   -  person Skeen    schedule 29.08.2013


Ответы (1)


Судя по приведенному коду, вы, похоже, ищете ошибку компилятора/библиотеки. Я не могу воспроизвести проблему ни с одной из следующих комбинаций:

Изменить Теперь включает clang++ и boost 1_49_0. Valgrind оказался чистым в выбранном количестве проверенных случаев.

  • clang++ 2.9, -O3, буст 1_46_1
  • clang++ 2.9, -O0, буст 1_46_1
  • clang++ 2.9, -O3, буст 1_48_0
  • clang++ 2.9, -O0, буст 1_48_0
  • clang++ 2.9, -O3, буст 1_49_0
  • clang++ 2.9, -O0, буст 1_49_0

  • gcc 4.4.5, -O0, буст 1_42_1

  • gcc 4.4.5, -O0, буст 1_46_1
  • gcc 4.4.5, -O0, буст 1_48_0
  • gcc 4.4.5, -O0, буст 1_49_0
  • gcc 4.4.5, -O3, буст 1_42_1
  • gcc 4.4.5, -O3, буст 1_46_1
  • gcc 4.4.5, -O3, буст 1_48_0
  • gcc 4.4.5, -O3, буст 1_49_0
  • gcc 4.6.1, -O0, буст 1_46_1
  • gcc 4.6.1, -O0, буст 1_48_0
  • gcc 4.6.1, -O0, буст 1_49_0
  • gcc 4.6.1, -O3, буст 1_42_1
  • gcc 4.6.1, -O3, буст 1_46_1
  • gcc 4.6.1, -O3, буст 1_48_0
  • gcc 4.6.1, -O3, буст 1_49_0

Полный протестированный код:

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

namespace qi    = boost::spirit::qi;
namespace lex   = boost::spirit::lex;

template <typename Lexer>
struct Tokens : lex::lexer<Lexer>
{
    Tokens();

    lex::token_def<std::string> identifier;
    lex::token_def<std::string> string;
    lex::token_def<bool> boolean;
    lex::token_def<double> real;
    lex::token_def<> comment;
    lex::token_def<> whitespace;
};

template <typename Lexer>
Tokens<Lexer>::Tokens()
{
    // Define regex macros
    this->self.add_pattern
        ("LETTER", "[a-zA-Z_]")
        ("DIGIT", "[0-9]")
        ("INTEGER", "-?{DIGIT}+")
        ("FLOAT", "-?{DIGIT}*\\.{DIGIT}+");

    // Define the tokens' regular expressions
    identifier = "{LETTER}({LETTER}|{DIGIT})*";
    string = "\"[a-zA-Z_0-9]*\"";
    boolean = "true|false";
    real = "{INTEGER}|{FLOAT}";
    comment = "#[^\n\r\f\v]*$";
    whitespace = "\x20\n\r\f\v\t+";

    // Define tokens
    this->self
        = identifier
        | string
        | boolean
        | real
        | '{'
        | '}'
        | '<'
        | '>';

    // Define tokens to be ignored
    this->self("WS")
        = whitespace
        | comment;
}

////////////////////////////////////////////////
typedef lex::lexertl::token<char const*> TokenType;
typedef lex::lexertl::actor_lexer<TokenType> LexerType;

int main(int argc, const char *argv[])
{
    Tokens<LexerType> tokens;

    std::string string = "9index";
    char const* first = string.c_str();
    char const* last = &first[string.size()];
    LexerType::iterator_type token = tokens.begin(first, last);
    LexerType::iterator_type end = tokens.end();

    typedef boost::iterator_range<char const*> iterator_range;
    const iterator_range& range = boost::get<iterator_range>(token->value());
    std::cout << std::string(range.begin(), range.end()) << std::endl;

    ++token;

    // Returns false ONLY if I uncomment the above code
    std::cout << "Next valid: " << std::boolalpha << token_is_valid(*token) << '\n'; 

    return 0;
}
person sehe    schedule 02.05.2012
comment
Спасибо, я проверю это на своей системе. - person May Oakes; 03.05.2012