Java Antlr:初学者';s不匹配的输入应为ID

Java Antlr:初学者';s不匹配的输入应为ID,java,antlr4,Java,Antlr4,作为一名初学者,当我从书中学习ANTLR4时,我尝试运行第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(

作为一名初学者,当我从书中学习ANTLR4时,我尝试运行第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
来匹配除
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
D:\Antlr\Ex\PropFile\Prop1>grun-PropFile-prop-tokens
driver=mysql
^Z
[@0,0:11='driver=mysql',1:0]
[@1,12:13='\r\n',1:12]
[@2,14:13='',,2:14]
第1行:0输入“driver=mysql”不匹配,需要ID
当我使用
字符串:'''.''.'''.''
时,它就可以工作了

我想知道我错在哪里,这样我以后就可以避免类似的错误


请给我一些建议,谢谢

由于ID和STRING都可以匹配以“driver”开头的输入文本,所以lexer将选择最长的匹配,即使ID规则排在第一位

所以,你有几个选择。最直接的方法是通过要求字符串以等号开头来消除ID和字符串之间的歧义(这是您的备选方案的工作方式)

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;
}
}

由于ID也将匹配字符串值,如果我想允许字符串作为值而不是双引号,如何实现?感谢您的详细回答。我误解了如果ID首先出现,那么当遇到歧义时,它将选择ID作为选项。还有一些人建议不要使用匹配字符串等所有内容的规则:(~[\r\n])+;在我的例子中,java属性文件只包含字符串(不带引号),所以我将遵循您的方法。