ANTLR4: как вводить токены

Я пытаюсь реализовать препроцессор для DSL, смоделированный по образцу CPP в коде/дополнениях. Однако я не использую фабрику токенов. Требуется ли один? Вызов emit(token) не вводит токены в поток токенов, как ожидалось.

Вот лексер:

// string-delimited path  
SPATH     :  '"' (~[\n\r])*? '"'
                {
                 emit();  // inject the current token
                 // launch another lexer on the include file, get tokens,
                 // emit them all at once here
                 List<CommonToken> tokens = Preprocessor.include(getText());
                 if (null != tokens) {
                   for (CommonToken tok : tokens) {
                     emit(tok);
                   }
                 }
               }
      ;

Вот метод включения:

@SuppressWarnings("unchecked")
public static List<CommonToken> include(String filename) {
    List<CommonToken> tokens = null;
    try (FileReader fr = openFile(filename.substring(1, filename.length() - 1));
            BufferedReader br = new BufferedReader(fr)) {
        ANTLRInputStream input = new ANTLRInputStream(br);
        PreprocessorLexer lexer = new PreprocessorLexer(input);

        tokens = (List<CommonToken>) lexer.getAllTokens();

    } catch (IOException ioe) {
        log.error("Can't load ~{}~", ioe.getLocalizedMessage());
    }
    return tokens;
}

person user2643128    schedule 01.08.2013    source источник


Ответы (1)


Вам нужно переопределить Lexer.nextToken, чтобы обеспечить эту функцию. В вашем лексере сохраните Deque<Token> введенных токенов, которые еще не были возвращены nextToken. Когда очередь пуста, ваша реализация nextToken должна вернуть следующий токен в соответствии с реализацией суперкласса.

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

private final Deque<Token> pendingTokens = new ArrayDeque<>();

@Override
public Token nextToken() {
    Token pending = pendingTokens.pollFirst();
    if (pending != null) {
        return pending;
    }

    Token next = super.nextToken();
    pending = pendingTokens.pollFirst();
    if (pending != null) {
        pendingTokens.addLast(next);
        return pending;
    }

    return next;
}
person Sam Harwell    schedule 01.08.2013
comment
Как вы эмитируете текущий токен, не заменяя его? Emit() отдает токен, чей toString() возвращает [@56,110:123='strategy.ccl',‹0›,7:15] Разве ‹0› не является номером токена, указывающим на несуществующий токен? - person user2643128; 01.08.2013
comment
Удаление emit() из правила лексера позволило правильно испускать текущий токен. - person user2643128; 01.08.2013
comment
Сокращение nextToken() от Token next... для возврата next; вернуть super.nextToken(); тоже помогло. - person user2643128; 01.08.2013
comment
@user2643128 user2643128 Сокращение nextToken() таким образом означает, что вы можете вставлять токены только после текущего токена. Как у меня это есть, вы можете вводить токены до или после текущего токена, хотя по умолчанию он будет вставляться раньше, и вам нужно быть умным, если вы хотите ввести потом. - person Sam Harwell; 02.08.2013