Antlr4语法不会解析多行输入

时间:2018-08-22 21:05:30

标签: antlr4

我想用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。)

我在做什么错了?

1 个答案:

答案 0 :(得分:2)

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

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

  

第3:0行多余的输入“ intProp1”,期望{,'type',PROPERTY_NAME,NEWLINE}
  第4:0行:多余的输入“ stringProp2”,期望{,'type',PROPERTY_NAME,NEWLINE}

因此,显然intProp1stringProp2不被识别为PROPERTY_NAME。因此,让我们看一下生成了哪些令牌(您可以使用-tokens的{​​{1}}选项,或者只是遍历代码中的令牌流来做到这一点)

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_NAME。实际上,目前尚不清楚什么应该将PROPERTY_NAMETYPE_NAME区别开来,所以现在让我们来看一下您的语法:

PROPERTY_NAME

在这里,您有三个词法器规则,它们的定义完全相同。这是一个不好的信号。

只要在当前输入上可以匹配多个词法分析器规则,ANTLR就会选择产生最长匹配项的规则,并在出现平局的情况下选择语法中最先出现的规则。这就是所谓的最大咀嚼规则。

如果具有相同定义的多个规则,则意味着这些规则将始终在同一输入上匹配,并且它们将始终产生相同长度的匹配。因此,根据最大规则,将始终使用第一个定义(TYPE_NAME : IDENTIFIER ; PROPERTY_NAME : IDENTIFIER ; PROPERTY_TYPE : IDENTIFIER ; fragment IDENTIFIER : (LETTER | '_') (LETTER | DIGIT | '_' )* ; ),其他定义也可能不存在。

问题基本上可以归结为以下事实:没有词法上可以区分不同类型的名称,因此,词法分析器没有基础可以确定给定标识符表示的名称类型。这告诉我们名称不应该是词法分析器规则。相反,TYPE_NAME应该是词法分析器规则,而IDENTIFIER应该是(有些不必要的)解析器规则或完全删除(您可以在当前使用{{1的任何地方使用FOO_NAME }}。