Тип токена зависит от следующего токена

Я застрял с довольно простой грамматикой. Поиск в Google и чтение книг не помогли. Я начал использовать ANTLR совсем недавно, так что, вероятно, это очень простой вопрос.

Я пытаюсь написать очень простой Lexer, используя ANTLR v3.

grammar TestLexer;

options {
  language = Java;
}

TEST_COMMENT
    :   '/*' WS? TEST WS? '*/'
    ;

ML_COMMENT
    :   '/*' ( options {greedy=false;} : .)* '*/' {$channel=HIDDEN;}
    ;

TEST    :   'TEST'
    ;

WS  :   (' ' | '\t' | '\n' | '\r' | '\f')+ {$channel=HIDDEN;}
    ;

Тестовый класс:

public class TestParserInvoker {
    private static void extractCommandsTokens(final String script) throws RecognitionException {

        final ANTLRStringStream input = new ANTLRStringStream(script);
        final Lexer lexer = new TestLexer(input);

        final TokenStream tokenStream = new CommonTokenStream(lexer);
        Token t;
        do {
            t = lexer.nextToken();
            if (t != null) {
                System.out.println(t);
            }
        } while (t == null || t.getType() != Token.EOF);
    }


    public static void main(final String[] args) throws RecognitionException {
        final String script = "/* TEST */";
        extractCommandsTokens(script);
    }
}

Поэтому, когда тестовая строка имеет вид "/* TEST */", лексер выдает два ожидаемых токена. Один с типом TEST_COMMENT и один с EOF. Все отлично.

Но если тестовая строка содержит один дополнительный пробел в конце: "/* TEST */ " лексер создает три токена: ML_COMMENT, WS и EOF.

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

Спасибо за помощь!

PS Я могу использовать опцию lexer filter=true - токен получит правильный тип, но этот подход требует дополнительной работы в определениях токенов. Если честно, я не хочу использовать этот тип лексера.

1 ответ

Решение

ANTLR токенизирует поток символов, начиная с верхнего правила и ниже, и старается максимально соответствовать. Так что да, я также ожидал TEST_COMMENT быть создан для обоих "/* TEST */" а также "/* TEST */ ", Вы всегда можете взглянуть на сгенерированный исходный код лексера, чтобы понять, почему он решил создать ML_COMMENT для второго входа.

Будь то ошибка или ожидаемое поведение, я бы не стал использовать отдельные правила лексера, которые выглядят очень похожими. Не могли бы вы объяснить, что вы действительно пытаетесь решить здесь?

user776872 написал (а):

Я могу использовать опцию lexer filter=true - токен получит правильный тип, но этот подход требует дополнительной работы в определениях токенов. Если честно, я не хочу использовать этот тип лексера.

Я не совсем понимаю это замечание. Вас интересует только часть входного источника? В таком случае, filter=true это, безусловно, хороший вариант. Если вы хотите токенизировать весь входной источник, вам не следует использовать filter=true,

РЕДАКТИРОВАТЬ

В случае проведения различия между многострочными комментариями и комментариями Javadoc, лучше оставить их в одном правиле и изменить тип токена, если он начинается с /** как это:

grammar T;

// options

tokens {
  DOC_COMMENT;
}

// rules

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;

Обратите внимание, что оба .* а также .+ по умолчанию не жадные в ANTLR (вопреки распространенному мнению).

демонстрация

grammar T;

tokens {
  DOC_COMMENT;
}

@parser::members {
  public static void main(String[] args) throws Exception {
    TLexer lexer = new TLexer(new ANTLRStringStream("/**/ /*foo*/ /**bar*/"));
    TParser parser = new TParser(new CommonTokenStream(lexer));
    parser.parse();
  }
}

parse
  :  (t=. {System.out.println(tokenNames[$t.type] + " :: " + $t.text);})* EOF
  ;

COMMENT
  :  '/*' (~'*' .*)? '*/'
  |  '/**' ~'/' .* '*/' {$type=DOC_COMMENT;}
  ;

SPACE
  :  ' ' {$channel=HIDDEN;}
  ;

который производит:

bart @ hades: ~ / Программирование /ANTLR/ Демоверсии /T$ java -cp antlr-3.3.jar org.antlr.Tool Tg
bart@hades:~/ Программирование /ANTLR/ Демоверсии /T$ javac -cp antlr-3.3.jar *.java
bart@hades:~/ Программирование /ANTLR/ Демоверсии /T$ java -cp .:antlr-3.3.jar TParser 
КОММЕНТАРИЙ:: /**/
КОММЕНТАРИЙ:: /*foo*/
DOC_COMMENT:: /** бар * /
Другие вопросы по тегам