1

我想使用 Antlr4 编写一个语法来解析一些定义,但我一直在努力让 Antlr 合作。

定义有两种线,一种类型和一种属性。我可以让我的语法正确解析类型行,但它要么忽略属性行,要么无法识别 PROPERTY_TYPE,具体取决于我如何调整语法。

这是我的语法(尝试#583):

    grammar TypeDefGrammar;

    start
        :   statement+ ;

    statement
        :   type NEWLINE
        |   property NEWLINE
        |   NEWLINE ;

    type
        :   TYPE_KEYWORD TYPE_NAME;             // e.g. 'type MyType1'

    property
        :   PROPERTY_NAME ':' PROPERTY_TYPE ;   // e.g. 'someProperty1: int'

    TYPE_KEYWORD
        :   'type' ;

    TYPE_NAME
        :   IDENTIFIER ;

    PROPERTY_NAME
        :   IDENTIFIER ;

    PROPERTY_TYPE
        :   IDENTIFIER ;

    fragment IDENTIFIER
        :   (LETTER | '_') (LETTER | DIGIT | '_' )* ;
    fragment LETTER
        :   [a-zA-Z] ;
    fragment DIGIT
        :   [0-9] ;

    NEWLINE
        :   '\r'? '\n' ;
    WS
        :   [ \t] -> skip ;

这是一个示例输入:

    type SimpleType

    intProp1: int
    stringProp2 : String

(返回类型但忽略 intProp1、stringProp2。)

我究竟做错了什么?

4

1 回答 1

3

通常,当规则不匹配整个输入,但匹配它的前缀时,它会简单地匹配该前缀并将输入的其余部分留在流中而不会产生错误。如果您希望您的规则始终匹配整个输入,您可以添加EOF到规则的末尾。这样,当它与整个输入不匹配时,您将收到正确的错误消息。

因此,让我们将您的start规则更改为start : statement+ EOF;. 现在应用于start您的输入将导致以下错误消息:

第 3:0 行无关输入 'intProp1' 期待 {, 'type', PROPERTY_NAME, NEWLINE}
第 4:0 行无关输入 'stringProp2' 期待 {, 'type', PROPERTY_NAME, NEWLINE}

很明显intProp1stringProp2不被认为是PROPERTY_NAMEs。因此,让我们看看生成了哪些令牌(您可以使用-tokens选项来执行此操作,grun或者仅通过迭代代码中的令牌流):

[@0,0:3='type',<'type'>,1:0]
[@1,5:14='SimpleType',<TYPE_NAME>,1:5]
[@2,15:15='\n',<NEWLINE>,1:15]
[@3,16:16='\n',<NEWLINE>,2:0]
[@4,17:24='intProp1',<TYPE_NAME>,3:0]
[@5,25:25=':',<':'>,3:8]
[@6,27:29='int',<TYPE_NAME>,3:10]
[@7,30:30='\n',<NEWLINE>,3:13]
[@8,31:41='stringProp2',<TYPE_NAME>,4:0]
[@9,43:43=':',<':'>,4:12]
[@10,45:50='String',<TYPE_NAME>,4:14]
[@11,51:51='\n',<NEWLINE>,4:20]
[@12,52:51='<EOF>',<EOF>,5:0]

所以代码中的所有标识符都被识别为TYPE_NAMEs,而不是PROPERTY_NAMEs。实际上,尚不清楚应该区分 aTYPE_NAME和 a PROPERTY_NAME,所以现在让我们实际看一下您的语法:

TYPE_NAME
    :   IDENTIFIER ;

PROPERTY_NAME
    :   IDENTIFIER ;

PROPERTY_TYPE
    :   IDENTIFIER ;

fragment IDENTIFIER
    :   (LETTER | '_') (LETTER | DIGIT | '_' )* ;

在这里,您有三个具有完全相同定义的词法分析器规则。这是一个不好的迹象。

每当多个词法分析器规则可以在当前输入上匹配时,ANTLR 会选择会产生最长匹配的那个,在出现平局的情况下选择语法中第一个出现的那个。这被称为最大咀嚼规则。

如果您有多个具有相同定义的规则,这意味着这些规则将始终在相同的输入上匹配,并且它们将始终产生相同长度的匹配。因此,根据最大数量规则,TYPE_NAME将始终使用第一个定义 ( ),而其他定义也可能不存在。

问题基本上归结为这样一个事实,即没有任何东西可以在词法上区分不同类型的名称,因此词法分析器无法确定给定标识符代表哪种类型的名称。这告诉我们名称不应该是词法分析器规则。相反IDENTIFIER应该是词法分析器规则,并且FOO_NAMEs 应该是(有点不必要的)解析器规则或完全删除(您可以IDENTIFIER在当前使用的任何地方使用FOO_NAME)。

于 2018-08-22T21:30:38.633 回答