Antlr: несоответствующий ввод начальный ожидающий идентификатор

1

Как новичок, когда я изучал ANTLR4 из The Definitive ANTLR 4 Reference, я попытался запустить мою модифицированную версию упражнения из главы 7:

/**
 * to parse properties file
 * this example demonstrates using embedded actions in code
 */
grammar PropFile;

@header  {
    import java.util.Properties;
}
@members {
    Properties props = new Properties();
}
file
    : 
    {
        System.out.println("Loading file...");
    }
        prop+
    {
        System.out.println("finished:\n"+props);
    }
    ;

prop
    : ID '=' STRING NEWLINE 
    {
        props.setProperty($ID.getText(),$STRING.getText());//add one property
    }
    ;

ID  : [a-zA-Z]+ ;
STRING  :(~[\r\n])+; //if use  STRING : '"' .*? '"'  everything is fine
NEWLINE :   '\r'?'\n' ;

Поскольку свойства Java - это только пара ключей, я использую STRING для сопоставления eveything, кроме NEWLINE (я не хочу, чтобы он просто поддерживал строки в двойных кавычках). При выполнении следующего предложения я получил:

D:\Antlr\Ex\PropFile\Prop1>grun PropFile prop -tokens
driver=mysql
^Z
[@0,0:11='driver=mysql',<3>,1:0]
[@1,12:13='\r\n',<4>,1:12]
[@2,14:13='<EOF>',<-1>,2:14]
line 1:0 mismatched input 'driver=mysql' expecting ID

Когда я использую STRING: '"'.*? '"', Это работает.

Я хотел бы знать, где я был неправ, чтобы избежать подобных ошибок в будущем.

Пожалуйста, дайте мне предложение, спасибо!

  • 0
    Так как ID будет совпадать со значением String, если я хочу разрешить строку в качестве значения, но не в двойных кавычках, как это сделать?
Теги:
antlr4

1 ответ

1
Лучший ответ

Поскольку оба ID и STRING могут соответствовать входному тексту, начинающемуся с "driver", lexer будет выбирать максимально возможное совпадение, даже если правило идентификатора будет первым.

Итак, у вас есть несколько вариантов. Самое непосредственное - устранить двусмысленность между ID и STRING (как это работает ваша альтернатива), требуя, чтобы строка начиналась с знака равенства.

file : prop+ EOF ;
prop : ID STRING NEWLINE ;

ID      : [a-zA-Z]+ ;
STRING  : '=' (~[\r\n])+;
NEWLINE : '\r'?'\n' ;

Затем вы можете использовать действие, чтобы обрезать знак равенства из текста токена.

В качестве альтернативы вы можете использовать предикат для устранения неоднозначности правил.

file : prop+ EOF ;
prop : ID '=' STRING NEWLINE ;

ID      : [a-zA-Z]+ ;
STRING  : { isValue() }? (~[\r\n])+; 
NEWLINE : '\r'?'\n' ;

где метод isValue смотрит назад на поток символов, чтобы убедиться, что он соответствует знаку равенства. Что-то вроде:

@members {
public boolean isValue() {
    int offset = _tokenStartCharIndex;
    for (int idx = offset-1; idx >=0; idx--) {
        String s = _input.getText(Interval.of(idx, idx));
        if (Character.isWhitespace(s.charAt(0))) {
            continue;
        } else if (s.charAt(0) == '=') {
            return true;
        } else {
            break;
        }
    }
    return false;
}
}
  • 0
    Спасибо за ваш подробный ответ. Я неправильно понял, что если сначала идет ID, то при возникновении неоднозначности он выберет ID в качестве выбора. И некоторые другие предлагают не использовать правило, которое соответствует всему, например, STRING: (~ [\ r \ n]) + ; В моем случае файл свойств java просто содержит String-String (не в кавычках), поэтому я буду следовать вашему методу.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню