解决可能存在的ANTLR语法歧义(和一般改进技巧)

解决可能存在的ANTLR语法歧义(和一般改进技巧),antlr,antlr3,abstract-syntax-tree,antlrworks,Antlr,Antlr3,Abstract Syntax Tree,Antlrworks,我在构建语法时遇到了一个问题,该语法能够解析Python3的AST转储格式,并将其转换为AST格式,这对我来说更容易使用。我决定编写一个ANTLR语法来实现这一点,但我在处理关键字块(但出于某种原因,只处理关键字块)时遇到了一个问题。我分离出关键字语法,如图所示: grammar kwds; options {output=AST;} keywords: 'keywords=['((', '?)keyword)*']' -> keyword* ; keyword : '

我在构建语法时遇到了一个问题,该语法能够解析Python3的AST转储格式,并将其转换为AST格式,这对我来说更容易使用。我决定编写一个ANTLR语法来实现这一点,但我在处理关键字块(但出于某种原因,只处理关键字块)时遇到了一个问题。我分离出关键字语法,如图所示:

grammar kwds;
options {output=AST;}

keywords:   'keywords=['((', '?)keyword)*']' -> keyword*
    ;

keyword :   'keyword(arg='STRING', value='str')'
    ;
str :   'Str(s='STRING')' -> STRING
    ;

STRING
    :  '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
    ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

EMPTYBRACKETS
    :   '[]';

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC
    :   '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ;

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
这是为了接受关键字列表(0或更多带有逗号分隔符),格式如关键字规则所示

如果您为上述语法提供以下(有效)输入

关键词=[关键词(arg='name',value=Str(s='UGA')),关键词(arg='rank',value=Str(s='2'))]

语法应该会认识到这一点

但是,使用我编写的“完整”python 3 AST格式语法(出于节省空间的目的,在中找到,上面的两条规则分别位于第106行和第109行),它使用几乎完全相同的语法规则,在解析上面显示的示例中的第一个关键字匹配后,令牌流似乎少了几个字符,在根据关键字规则进行解析时生成以下输出:

sample3.txt line 1:52 mismatched character 'e' expecting 'w'
sample3.txt line 1:53 no viable alternative at character 'y'
sample3.txt line 1:54 no viable alternative at character 'w'
sample3.txt line 1:55 no viable alternative at character 'o'
sample3.txt line 1:56 no viable alternative at character 'r'
sample3.txt line 1:57 no viable alternative at character 'd'
sample3.txt line 1:58 no viable alternative at character '('
sample3.txt line 1:59 missing ENDBR at 'arg='
我只能想到发生这种情况的一种可能性:由于语法中的歧义,某些东西被错误地标记,因为我用来检测多个关键字语句的模式适用于其他类型的语句。然而,我完全无法理解这种歧义在语法中的实际位置

此外,任何关于如何提高我的语法的一般改进建议都将不胜感激

如果添加规则:

parse
 : (t=. {System.out.printf("type=\%-20s text='\%s'\n", tokenNames[$t.type], $t.text);})* EOF
 ;
只需匹配零个或多个标记并打印出这些标记的类型和文本,您就会看到lexer无法处理示例中的输入
,关键字

keywords=[keyword(arg='name', value=Str(s='UGA')), keyword(arg='rank', value=Str(s='2'))]
                                                 ^^^^^^^^^
因此,您的解析器规则中没有任何一条是有问题的,但是在词法层面上出现了问题

我建议您从解析器中删除所有文字标记,并为它们创建lexer规则。然后添加一个我在上面发布的
parse
规则,您可以使用它测试lexer,看看是否创建了正确的标记。一旦创建了正确的标记,就编写解析器规则

我很确定这里的问题是,你没有一个
,关键字'
令牌,而且,一旦lexer“看到”
,k'
,它就会尝试创建一个
,kwargs'
令牌,当然失败了。因此,我还建议您不要在标记中包含逗号和空格,而是让它们成为自己的标记

此外,您不希望有这样的重写规则:

stmtlist:       ((', '?) stmt)* -> stmt*
        ;
这可能与什么都不匹配。如果发生这种情况,ANTLR将在创建AST时引发异常。始终让重写规则生成某些内容:


啊哈,我明白了。这说明lexer会混淆“k”,这也解释了一些字符是如何被消费的,而另一些字符则不是。另外,感谢您提供有关重写规则的提示!
...

tokens {
  ...
  STMTLST;
  ...
}

...

stmtlist:       ((', '?) stmt)* -> ^(STMTLST stmt*)
        ;