Как преобразовать значение токена 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" (он читает первое число, оставляя "индекс" в потоке). Если я распечатаю значение строки (first, last) в этой точке, оно показывает "ndex". По какой-то причине лексер не справляется с этим символом "я"?

Я даже пытался использовать 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(), должен существовать до тех пор, пока вы ожидаете чтения токенов.

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

1 ответ

Решение

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

Редактировать сейчас включает в себя clang ++ и boost 1_49_0. Valgrind подходит для выбранного количества проверенных случаев.

  • лязг ++ 2.9, -O3, буст 1_46_1
  • лязг ++ 2.9, -O0, буст 1_46_1
  • лязг ++ 2.9, -O3, буст 1_48_0
  • clang ++ 2,9, -O0, повышение 1_48_0
  • лязг ++ 2.9, -O3, буст 1_49_0
  • clang ++ 2,9, -O0, повышение 1_49_0

  • gcc 4.4.5, -O0, boost 1_42_1

  • gcc 4.4.5, -O0, boost 1_46_1
  • gcc 4.4.5, -O0, boost 1_48_0
  • gcc 4.4.5, -O0, boost 1_49_0
  • gcc 4.4.5, -O3, boost 1_42_1
  • gcc 4.4.5, -O3, boost 1_46_1
  • gcc 4.4.5, -O3, boost 1_48_0
  • gcc 4.4.5, -O3, boost 1_49_0
  • gcc 4.6.1, -O0, boost 1_46_1
  • gcc 4.6.1, -O0, boost 1_48_0
  • gcc 4.6.1, -O0, boost 1_49_0
  • gcc 4.6.1, -O3, boost 1_42_1
  • gcc 4.6.1, -O3, boost 1_46_1
  • gcc 4.6.1, -O3, boost 1_48_0
  • gcc 4.6.1, -O3, boost 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;
}
Другие вопросы по тегам