解决ANTLR词法分析器规则歧义的正确方法是什么?

时间:2012-01-26 18:08:03

标签: antlr grammar

请参阅以下网址提供的源代码:https://gist.github.com/1684022

我定义了两个令牌:

ID  :   ('a'..'z' | 'A'..'Z') ('0'..'9' | 'a'..'z' | 'A'..'Z' | ' ')*;

PITCH   
    :   (('A'|'a') '#'?)
    |   (('B'|'b') '#'?) 
    |   (('C'|'c') '#'?);

显然,字母“A”会含糊不清。

我进一步定义:

note    :   PITCH;
name    :   ID;
main    :   name ':' note '\n'?

现在,如果我输入“A:A”作为解析器的输入,我总是会收到错误。解析器需要PITCH或ID,具体取决于是先定义ID还是PITCH:

mismatched input 'A' expecting ID

解决此问题的正确方法是什么,以使其按预期工作?


如上所述,尽管直观地了解解析应该如何工作,但ANTLR并没有做“正确的事情”。也就是说,即使main规则说name / ID应该先出现,但词法分析员似乎对此一无所知并将“A”标识为PITCH,因为它遵循“最长匹配”/“先到先得”的规则,而不是更合理的“规则所说的”规则。

通过匹配ID和PITCH,然后像dasblinkenlight所说的那样重新组合它们,唯一的解决方法是伪造/破解吗?

1 个答案:

答案 0 :(得分:5)

以下是我如何重新考虑这种语法以使其有效:

ID  :   (('a'..'z' | 'A'..'Z') ('0'..'9' | 'a'..'z' | 'A'..'Z' | ' ')+)
    |   ('d'..'z' | 'D'..'Z');

PITCH : 'a'..'c' | 'A'..'C';

SHARP : '#';

note    :   PITCH SHARP?;

name    :   ID | PITCH;

main    :   name ':' note '\n'? EOF

这将长名称与单字符间距名称分开,这些名称在解析器中“重新统一”。此外,“sharp”令牌也有自己的名称,并在解析器中被识别为可选令牌。