ANTLR:过于贪婪的规则
ANTLR: too greedy rule
看来我在理解过于贪婪的规则匹配时遇到了问题。我正在尝试对 .g4
文件进行 lex 语法着色。这是使此问题可重现的最小(简化)摘录:
lexer grammar ANTLRv4Lexer;
Range
: '[' RangeChar+ ']'
;
fragment EscapedChar
: '\' ~[u]
| '\u' EscapedCharHex EscapedCharHex EscapedCharHex EscapedCharHex
;
fragment EscapedCharHex
: [0-9A-Fa-f]
;
fragment RangeChar
: ~']'
| EscapedChar
;
Punctuation
: [:;()+\->*[\]~|]
;
Identifier
: [a-zA-Z0-9]+
;
Whitespace
: [ \t]+
-> skip
;
Newline
: ( '\r' '\n'?
| '\n'
)
-> skip
;
LineComment
: '//' ~[\r\n]*
;
(不完整的)测试文件如下:
: (~ [\]\] | EscAny)+ -> more
;
// ------
fragment Id
: NameStartChar NameChar*
;
String2Part
: ( ~['\]
| EscapeSequence
)+
;
不明白为什么匹配Range
这么贪心:
[@0,3:3=':',<Punctuation>,1:3]
[@1,5:5='(',<Punctuation>,1:5]
[@2,6:6='~',<Punctuation>,1:6]
[@3,8:135='[\]\] | EscAny)+ -> more\r\n ;\r\n\r\n // ------\r\n\r\nfragment Id\r\n : NameStartChar NameChar*\r\n ;\r\n\r\n\r\nString2Part\r\n\t: ( ~['\]',<Range>,1:8]
[@4,141:141='|',<Punctuation>,13:3]
[@5,143:156='EscapeSequence',<Identifier>,13:5]
[@6,162:162=')',<Punctuation>,14:3]
[@7,163:163='+',<Punctuation>,14:4]
[@8,167:167=';',<Punctuation>,15:1]
[@9,170:169='<EOF>',<EOF>,16:0]
我明白为什么在第一行它匹配[
、\]
和\
,但为什么它明明把]
当作RangeChar
?
您的词法分析器使用 ~']'
选项匹配 \]
中的第一个 \
,然后将剩余的 \]
作为 EscapedChar
匹配。它这样做的原因是这种解释导致比 \
是 EscapedChar
和 ]
是范围的结尾并且有多种有效方法的匹配更长的匹配匹配词法分析器规则,ANTLR 总是选择最长的(除非涉及 *?
)。
要解决此问题,您应该更改 RangeChar
,以便反斜杠仅允许作为转义序列的一部分,即将 ~']'
替换为 ~[\]\]
。
看来我在理解过于贪婪的规则匹配时遇到了问题。我正在尝试对 .g4
文件进行 lex 语法着色。这是使此问题可重现的最小(简化)摘录:
lexer grammar ANTLRv4Lexer;
Range
: '[' RangeChar+ ']'
;
fragment EscapedChar
: '\' ~[u]
| '\u' EscapedCharHex EscapedCharHex EscapedCharHex EscapedCharHex
;
fragment EscapedCharHex
: [0-9A-Fa-f]
;
fragment RangeChar
: ~']'
| EscapedChar
;
Punctuation
: [:;()+\->*[\]~|]
;
Identifier
: [a-zA-Z0-9]+
;
Whitespace
: [ \t]+
-> skip
;
Newline
: ( '\r' '\n'?
| '\n'
)
-> skip
;
LineComment
: '//' ~[\r\n]*
;
(不完整的)测试文件如下:
: (~ [\]\] | EscAny)+ -> more
;
// ------
fragment Id
: NameStartChar NameChar*
;
String2Part
: ( ~['\]
| EscapeSequence
)+
;
不明白为什么匹配Range
这么贪心:
[@0,3:3=':',<Punctuation>,1:3]
[@1,5:5='(',<Punctuation>,1:5]
[@2,6:6='~',<Punctuation>,1:6]
[@3,8:135='[\]\] | EscAny)+ -> more\r\n ;\r\n\r\n // ------\r\n\r\nfragment Id\r\n : NameStartChar NameChar*\r\n ;\r\n\r\n\r\nString2Part\r\n\t: ( ~['\]',<Range>,1:8]
[@4,141:141='|',<Punctuation>,13:3]
[@5,143:156='EscapeSequence',<Identifier>,13:5]
[@6,162:162=')',<Punctuation>,14:3]
[@7,163:163='+',<Punctuation>,14:4]
[@8,167:167=';',<Punctuation>,15:1]
[@9,170:169='<EOF>',<EOF>,16:0]
我明白为什么在第一行它匹配[
、\]
和\
,但为什么它明明把]
当作RangeChar
?
您的词法分析器使用 ~']'
选项匹配 \]
中的第一个 \
,然后将剩余的 \]
作为 EscapedChar
匹配。它这样做的原因是这种解释导致比 \
是 EscapedChar
和 ]
是范围的结尾并且有多种有效方法的匹配更长的匹配匹配词法分析器规则,ANTLR 总是选择最长的(除非涉及 *?
)。
要解决此问题,您应该更改 RangeChar
,以便反斜杠仅允许作为转义序列的一部分,即将 ~']'
替换为 ~[\]\]
。