我使用ANTLR4生成解析器。我是解析器语法的新手。我已经阅读了非常有帮助的ANTLR Mega Tutorial,但我仍然坚持如何正确地订购(和/或编写)我的词法分析器和解析器规则。
我希望解析器能够处理这样的事情:
Hello<<名称>>,你好吗?
在运行时我将替换"<<名称>>"用户名。
所以大多数情况下我正在解析文字(以及标点符号,符号等),偶尔会出现"<<某事>>"标签,我称之为" func"在我的词法规则中。
这是我的语法:
doc: item* EOF ;
item: (func | WORD) PUNCT? ;
func: '<<' ID '>>' ;
WS : [ \t\n\r] -> skip ;
fragment LETTER : [a-zA-Z] ;
fragment DIGIT : [0-9] ;
fragment CHAR : (LETTER | DIGIT | SYMB ) ;
WORD : CHAR+ ;
ID: LETTER ( LETTER | DIGIT)* ;
PUNCT : [.,?!] ;
fragment SYMB : ~[a-zA-Z0-9.,?! |{}<>] ;
旁注:我添加了#34; PUNCT?&#34;在&#34;项目结束时#34;规则,因为有可能,例如在我上面给出的例句中,有一个逗号出现在&#34; func&#34;之后。但是因为你也可以在一个&#34; WORD&#34;之后有一个逗号。然后我决定把标点符号放在&#34; item&#34;而不是在&#34; func&#34;和&#34; WORD&#34;。
如果我在上面的句子上运行这个解析器,我会得到一个如下所示的解析树:
以红色突出显示的任何内容都是解析错误。
所以它没有认识到&#34; ID&#34;在双角括号内作为&#34; ID&#34;。大概这是因为&#34; WORD&#34;在我的词法分析器规则列表中排在第一位。但是,我没有说&#34;&lt;&lt; WORD&gt;&gt;&#34;,只有一个规则说&#34;&lt;&lt; ID&gt;&gt;&#34;,所以我不清楚为什么会这样。
如果我交换&#34; ID&#34;的顺序和&#34; WORD&#34;在我的语法中,现在它们按顺序排列:
ID: LETTER ( LETTER | DIGIT)* ;
WORD : CHAR+ ;
所以现在&#34; func&#34;和&#34; ID&#34;规则正在得到妥善处理,但没有一个人被认可。
我如何克服这个难题?
我想一个选项可能是改变&#34; func&#34;规则为&#34;&lt;&lt; WORD&gt;&gt;&#34;并且只是将所有内容视为单词,废除&#34; ID&#34;。但我想区分文本字和变量标识符(例如,变量标识符中不允许使用特殊字符)。
感谢您的帮助!
答案 0 :(得分:1)
由于他的评论中已经提到的“500 - 内部服务器错误”,ANTLR将按照语法中定义的顺序匹配词法规则(最顶层的规则将首先匹配),如果某个输入已经匹配,则ANTLR胜出'尝试以不同的方式匹配它。
在您的情况下,Current.DispatcherUnhandledException
和WORD
规则都可以匹配ID
之类的输入,但首先声明abc
WORD
将始终匹配为abc
而不是WORD
。实际上ID
永远不会匹配,因为ID
无法匹配ID
的有效输入。
但是,如果您的唯一目标是替换WORD
和<<
之间的任何内容,那么最好使用正则表达式。但是,如果你仍想使用ANTLR,你应该减少你的语法,只关心基本要素。这是为了区分>>
和<<
之间的任何输入和输入。因此你的语法应该是这样的:
>>
或者您可以完全跳过start: (INTERESTING | UNINTERESTING) ;
INTERESTING: '<<' .*? '>>' ;
UNINTERESTING: (~[<])+ | '<' ;
。
答案 1 :(得分:1)
来自The Definitive ANTLR 4 Reference:
ANTLR通过解决词汇歧义 将输入字符串与语法中第一个指定的规则相匹配。
使用语法(在Question.g4中)和包含
的t.text文件Hello << name >>, how are you at nine o'clock?
执行
$ grun Question doc -tokens -diagnostics t.text
给出
[@0,0:4='Hello',<WORD>,1:0]
[@1,6:7='<<',<'<<'>,1:6]
[@2,9:12='name',<WORD>,1:9]
[@3,14:15='>>',<'>>'>,1:14]
[@4,16:16=',',<PUNCT>,1:16]
[@5,18:20='how',<WORD>,1:18]
[@6,22:24='are',<WORD>,1:22]
[@7,26:28='you',<WORD>,1:26]
[@8,30:31='at',<WORD>,1:30]
[@9,33:36='nine',<WORD>,1:33]
[@10,38:44='o'clock',<WORD>,1:38]
[@11,45:45='?',<PUNCT>,1:45]
[@12,47:46='<EOF>',<EOF>,2:0]
line 1:9 mismatched input 'name' expecting ID
line 1:14 extraneous input '>>' expecting {<EOF>, '<<', WORD, PUNCT}
现在在WORD
规则中将word
更改为item
,然后添加word
规则:
item: (func | word) PUNCT? ;
word: WORD | ID ;
并在WORD之前输入ID:
ID: LETTER ( LETTER | DIGIT)* ;
WORD : CHAR+ ;
令牌现在是
[@0,0:4='Hello',<ID>,1:0]
[@1,6:7='<<',<'<<'>,1:6]
[@2,9:12='name',<ID>,1:9]
[@3,14:15='>>',<'>>'>,1:14]
[@4,16:16=',',<PUNCT>,1:16]
[@5,18:20='how',<ID>,1:18]
[@6,22:24='are',<ID>,1:22]
[@7,26:28='you',<ID>,1:26]
[@8,30:31='at',<ID>,1:30]
[@9,33:36='nine',<ID>,1:33]
[@10,38:44='o'clock',<WORD>,1:38]
[@11,45:45='?',<PUNCT>,1:45]
[@12,47:46='<EOF>',<EOF>,2:0]
并且没有更多错误。如-gui图形所示,您现在已将分支标识为word
或func
。