使用 ANTLR4 在语法中排序词法分析器规则

Ordering lexer rules in a grammar using ANTLR4

我正在使用 ANTLR4 生成解析器。我是解析器语法的新手。我已经阅读了非常有帮助的 ANTLR Mega Tutorial 但我仍然坚持如何正确排序(and/or 编写)我的词法分析器和解析器规则。

我希望解析器能够处理这样的事情:

你好<<名字>>,你好吗?

在运行时我会用用户名替换“<<名称>>”。

所以大部分时间我都在解析文本单词(和标点符号、符号等),除了偶尔的“<< something >>”标签,我在我的词法分析器规则中称之为 "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.,?! |{}<>] ;

旁注:我在 "item" 规则的末尾添加了 "PUNCT?" 是因为有可能,例如在我上面给出的例句中,在 "func"。但是由于 "WORD" 之后也可以有逗号,所以我决定将标点符号放在 "item" 中,而不是同时放在 "func" 和 "WORD".

如果我 运行 这个解析器在上面的句子中,我得到一个如下所示的解析树:

任何以红色突出显示的内容都是解析错误。

所以它没有将双尖括号内的 "ID" 识别为 "ID"。大概这是因为 "WORD" 在我的词法分析器规则列表中排在第一位。但是,我没有说“<< WORD >>”的规则,只有说“<< ID >>”的规则,所以我不清楚为什么会这样。

如果我在语法中交换 "ID" 和 "WORD" 的顺序,那么现在它们的顺序是:

ID: LETTER ( LETTER | DIGIT)* ;
WORD : CHAR+ ;

和 运行 解析器,我得到一个这样的解析树:

所以现在 "func" 和 "ID" 规则得到了适当的处理,但是 "WORD" 中的 none 被识别了。

我该如何克服这个难题?

我想一个选择可能是将 "func" 规则更改为“<< WORD >>”,并将所有内容都视为单词,取消 "ID"。但是我想区分文本单词和变量标识符(例如,变量标识符中不允许使用特殊字符)。

感谢您的帮助!

正如他的评论中已经提到的“500 - 内部服务器错误”ANTLR 将按照它们在语法中定义的顺序匹配词法分析器规则(最上面的规则将首先匹配)并且如果某个输入已匹配 ANTLR不会尝试以不同的方式匹配它。

在您的情况下,WORDID 规则都可以匹配 abc 之类的输入,但由于首先声明 WORD,因此 abc 将始终匹配作为 WORD 而从不作为 ID。事实上,ID 将永远不会被匹配,因为没有 ID 无法被 WORD 匹配的有效输入。

但是,如果您的唯一目标是替换 <<>> 之间的任何内容,您最好使用正则表达式。但是,如果您仍想为此使用 ANTLR,则应减少语法以仅关心要点。即区分<<>>之间的任何输入和输入。因此你的语法应该是这样的:

start: (INTERESTING | UNINTERESTING) ;
INTERESTING: '<<' .*? '>>' ;
UNINTERESTING: (~[<])+ | '<' ;

或者您可以完全跳过 UNINTERESTING

来自 The Definitive ANTLR 4 Reference :

ANTLR resolves lexical ambiguities by matching the input string to the rule specified first in the grammar.

你的语法(在 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}

现在将 item 规则中的 WORD 更改为 word,并添加一个 word 规则:

item: (func | word) PUNCT? ;
word: WORD | ID ;

并将 ID 放在 WORD 之前:

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 图形所示,您现在有标识为 wordfunc.

的分支