У меня была похожая потребность в моей грамматике , где я хотел выпустить несколько токенов (фактически 2) для одного совпадения при определенных условиях (здесь: когда за точкой непосредственно следует идентификатор, включаяключевое слово).
// Special rule that should also match all keywords if they are directly preceded by a dot.
// Hence it's defined before all keywords.
// Here we make use of the ability in our base lexer to emit multiple tokens with a single rule.
DOT_IDENTIFIER:
DOT_SYMBOL LETTER_WHEN_UNQUOTED_NO_DIGIT LETTER_WHEN_UNQUOTED* { emitDot(); } -> type(IDENTIFIER)
;
A вспомогательная функция необходима для выдачи дополнительных токенов:
/**
* Puts a DOT token onto the pending token list.
*/
void MySQLBaseLexer::emitDot() {
_pendingTokens.emplace_back(_factory->create({this, _input}, MySQLLexer::DOT_SYMBOL, _text, channel,
tokenStartCharIndex, tokenStartCharIndex, tokenStartLine,
tokenStartCharPositionInLine));
++tokenStartCharIndex;
}
, что в свою очередь требует специальной обработкиизготовление токенов.Вы должны переопределить метод nextToken
в вашем потоке токенов, чтобы рассмотреть список ожидающих токенов перед возвратом следующего реального токена.
/**
* Allow a grammar rule to emit as many tokens as it needs.
*/
std::unique_ptr<antlr4::Token> MySQLBaseLexer::nextToken() {
// First respond with pending tokens to the next token request, if there are any.
if (!_pendingTokens.empty()) {
auto pending = std::move(_pendingTokens.front());
_pendingTokens.pop_front();
return pending;
}
// Let the main lexer class run the next token recognition.
// This might create additional tokens again.
auto next = Lexer::nextToken();
if (!_pendingTokens.empty()) {
auto pending = std::move(_pendingTokens.front());
_pendingTokens.pop_front();
_pendingTokens.push_back(std::move(next));
return pending;
}
return next;
}
Имейте в виду: правило лексера все ещевыдает свой собственный токен (здесь я установил IDENTIFIER
), что означает, что вам нужно только выдать дополнительные токены.