Как заставить ANTLR генерировать NoViableAltException? - PullRequest
5 голосов
/ 17 февраля 2010

Я работаю с antlr 3.2. У меня есть простая грамматика, состоящая из атомов (которые являются символами «0» или «1»), и правило, которое собирает их список в виде списка через запятую.

Когда я передаю "00" в качестве ввода, я не получаю ошибку, которая удивляет меня, потому что это не должно быть допустимым вводом:

C:\Users\dan\workspace\antlrtest\test>java -cp antlr-3.2.jar org.antlr.Tool Test.g
C:\Users\dan\workspace\antlrtest\test>javac -cp antlr-3.2.jar *.java
C:\Users\dan\workspace\antlrtest\test>java -cp .;antlr-3.2.jar TestParser
[0]

Как я могу заставить ошибку генерироваться в этом случае? Это особенно удивительно, потому что когда я использую интерпретатор в ANTLRWorks для этого ввода, он показывает исключение NoViableAltException.

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

Вот грамматика, которая является автономной и запускаемой:

grammar Test;

@parser::members {
  public static void main(String[] args) throws Exception {
    String text = "00";
    ANTLRStringStream in = new ANTLRStringStream(text);
    TestLexer lexer = new TestLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    System.out.println(new TestParser(tokens).mainRule());
  }
}

mainRule returns [List<String> words]
@init{$words = new ArrayList<String>();}
  :  w=atom {$words.add($w.text);} (',' w=atom {$words.add($w.text);} )*
  ;


atom: '0' | '1';

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

1 Ответ

2 голосов
/ 17 февраля 2010

После вашего mainRule вы должны добавить токен EOF, в противном случае ANTLR прекратит синтаксический анализ, когда нет действительного токена для сопоставления.

Кроме того, правило atom должно действительно быть правилом лексера, а не парсера (правила лексера начинаются с заглавной буквы).

Попробуйте вместо этого:

grammar Test;

@parser::members {
  public static void main(String[] args) throws Exception {
    String text = "0,1  ,  1  , 0,1";
    ANTLRStringStream in = new ANTLRStringStream(text);
    TestLexer lexer = new TestLexer(in);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    System.out.println(new TestParser(tokens).mainRule());
  }
}

mainRule returns [List<String> words]
@init{$words = new ArrayList<String>();}
  :  w=Atom {$words.add($w.text);} (',' w=Atom {$words.add($w.text);} )* EOF
  ;

Atom
  :  '0' | '1'
  ;

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

EDIT

Для уточнения: как вы уже выяснили, EOF не является обязательным. Это только заставит парсер пройти весь ввод. NoViableAltException генерируется только тогда, когда лексер натыкается на токен / символ, который не обрабатывается вашей лексерской грамматикой. Поскольку в вашей грамматике определены три токена (0, 1 и ,) и , ваш ввод "00" не содержит символов, не обработанных вашим грамматика, NoViableAltException не выбрасывается. Если вы измените свой ввод на что-то вроде "0?0", то появится NoViableAltException.

Поскольку ваш синтаксический анализатор находит первый 0, а затем не находит ,, он просто прекращает синтаксический анализ, так как вы не «сказали» ему анализировать весь путь до конца файла.

Надеюсь, это прояснит ситуацию. Если нет, дайте мне знать.

...