Antlr:初学者的输入期望ID不匹配
问题内容:
作为一个初学者,当我从《权威的ANTLR 4参考》一书中学习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
用来匹配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 : '"' .*? '"'
它时,它可以工作。
我想知道我错在哪里,以便将来避免类似的错误。
请给我一些建议,谢谢!
问题答案:
由于ID和STRING都可以匹配以“ driver”开头的输入文本,因此,即使ID规则排在最前面,词法分析器也会选择最长的匹配项。
因此,您在这里有几种选择。最直接的方法是通过要求字符串以等号开头来消除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;
}
}