antlr буквальное сопоставление строк: что я делаю неправильно?

Пользуюсь antlr 3 дня. Я могу анализировать выражения, писать Listeners, интерпретировать деревья синтаксического анализа... мечта сбылась.

Но затем я попытался сопоставить буквальную строку «foo%», и у меня не получилось. Я могу найти множество примеров, которые утверждают, что делают это. Я пробовал их все.

Поэтому я создал крошечный проект, чтобы соответствовать буквальной строке. Должно быть, я делаю что-то глупое.

grammar Test;

clause
  : stringLiteral EOF
  ;

fragment ESCAPED_QUOTE : '\\\'';
stringLiteral :   '\'' ( ESCAPED_QUOTE | ~('\n'|'\r') ) + '\'';

Простой тест:

public class Test {

    @org.junit.Test
    public void test() {
        String input = "'foo%'";
        TestLexer lexer = new TestLexer(new ANTLRInputStream(input));
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        TestParser parser = new TestParser(tokens);
        ParseTree clause = parser.clause();
        System.out.println(clause.toStringTree(parser));

        ParseTreeWalker walker = new ParseTreeWalker();
    }
}

Результат:

Running com.example.Test
line 1:1 token recognition error at: 'f'
line 1:2 token recognition error at: 'o'
line 1:3 token recognition error at: 'o'
line 1:4 token recognition error at: '%'
line 1:6 no viable alternative at input '<EOF>'
(clause (stringLiteral ' ') <EOF>)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.128 sec - in com.example.Test

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

Полное дерево сборки с поддержкой maven доступно для быстрого ознакомления здесь

31 строка кода... большая часть позаимствована из небольших примеров.

 $ mvn clean test

Использование antlr-4.5.2-1.


person Eric Newton    schedule 28.03.2016    source источник
comment
Ваша ошибка в том, что stringLiteral является правилом синтаксического анализатора, но оно должно быть правилом лексера. Начинайте его имя с заглавной буквы (или просто напишите его вообще заглавными буквами, так его легче определить как правило лексера).   -  person Lucas Trzesniewski    schedule 28.03.2016
comment
Благодарю вас! Это делает проблему совершенно ясной!   -  person Eric Newton    schedule 28.03.2016


Ответы (1)


Правила fragment могут использоваться только другими правилами лексера. Итак, вам нужно сделать stringLiteral правилом лексера, а не правилом парсера. Просто начните с заглавной буквы.

Кроме того, лучше расширить свой класс ~('\n'|'\r') с отрицанием, включив в него обратную косую черту и кавычку, и вы можете включить обратную косую черту, чтобы ее можно было экранировать:

clause
  : StringLiteral EOF
  ;

StringLiteral :   '\'' ( Escape | ~('\'' | '\\' | '\n' | '\r') ) + '\'';

fragment Escape : '\\' ( '\'' | '\\' );
person Bart Kiers    schedule 28.03.2016
comment
Благодарю вас! Одна небольшая разница (заглавные буквы) имела значение, и теперь я понимаю, что отличает лексер от правил парсера! - person Eric Newton; 28.03.2016