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