我想用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。)
我在做什么错了?
答案 0 :(得分:2)
通常,当一条规则与整个输入不匹配,但与它的前缀匹配时,它将简单地匹配该前缀,并将其余输入保留在流中而不会产生错误。如果您希望规则始终与整个输入匹配,则可以在规则末尾添加EOF
。这样,当它与整个输入不匹配时,您将收到正确的错误消息。
因此,让我们将您的start
规则更改为start : statement+ EOF;
。现在将start
应用于您的输入将导致以下错误消息:
第3:0行多余的输入“ intProp1”,期望{,'type',PROPERTY_NAME,NEWLINE}
第4:0行:多余的输入“ stringProp2”,期望{,'type',PROPERTY_NAME,NEWLINE}
因此,显然intProp1
和stringProp2
不被识别为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_NAME
与TYPE_NAME
区别开来,所以现在让我们来看一下您的语法:
PROPERTY_NAME
在这里,您有三个词法器规则,它们的定义完全相同。这是一个不好的信号。
只要在当前输入上可以匹配多个词法分析器规则,ANTLR就会选择产生最长匹配项的规则,并在出现平局的情况下选择语法中最先出现的规则。这就是所谓的最大咀嚼规则。
如果具有相同定义的多个规则,则意味着这些规则将始终在同一输入上匹配,并且它们将始终产生相同长度的匹配。因此,根据最大规则,将始终使用第一个定义(TYPE_NAME
: IDENTIFIER ;
PROPERTY_NAME
: IDENTIFIER ;
PROPERTY_TYPE
: IDENTIFIER ;
fragment IDENTIFIER
: (LETTER | '_') (LETTER | DIGIT | '_' )* ;
),其他定义也可能不存在。
问题基本上可以归结为以下事实:没有词法上可以区分不同类型的名称,因此,词法分析器没有基础可以确定给定标识符表示的名称类型。这告诉我们名称不应该是词法分析器规则。相反,TYPE_NAME
应该是词法分析器规则,而IDENTIFIER
应该是(有些不必要的)解析器规则或完全删除(您可以在当前使用{{1的任何地方使用FOO_NAME
}}。