Antlr 太贪婪的规则

Antlr 太贪婪的规则,antlr,antlr4,Antlr,Antlr4,看起来我在理解过于贪婪的规则匹配时遇到了问题。我正在尝试将一个.g4文件用于语法着色。以下是使该问题重现的最小(简化)摘录: lexer grammar ANTLRv4Lexer; Range : '[' RangeChar+ ']' ; fragment EscapedChar : '\\' ~[u] | '\\u' EscapedCharHex EscapedCharHex EscapedCharHex EscapedCharHex ; fr

看起来我在理解过于贪婪的规则匹配时遇到了问题。我正在尝试将一个
.g4
文件用于语法着色。以下是使该问题重现的最小(简化)摘录:

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
      )+
    ;
我不明白为什么它与
范围
匹配得如此贪婪:

[@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]
[@0,3:3=':',1:3]
[@1,5:5='(',,1:5]
[@2,6:6='~',,1:6]
[@3,8:135='[\]\\]\124; EscAny)+->更多\r\n\r\n\r\n/----\r\n\r\n片段Id\r\n:NameStartChar NameChar*\r\n\r\n\r\n\r\nString2Part\r\n\t:(~['\\]',1:8]
[@4,141:141='|',,13:3]
[@5143:156='EscapeSequence',13:5]
[@6,162:162=')',,14:3]
[@7,163:163='+',,14:4]
[@8,167:167=';',,15:1]
[@9,170:169='',,16:0]

我理解为什么在第一行中它与
[
\]
\\
匹配,但是为什么它显然将
]
视为
RangeChar

您的lexer使用
~']
替代项匹配
\\\]
中的第一个
\
,然后将剩余的
\]
作为
转义字符匹配。它这样做的原因是,这种解释导致的匹配比
\\
转义字符
]
是范围结束的情况更长,并且当有多种有效方法匹配词法规则时,ANTLR总是选择最长的一种(除非涉及
*?


要解决此问题,您应该更改
RangeChar
,以便反斜杠只允许作为转义序列的一部分,即将
~']'
替换为
~[\]\\]

您的lexer使用
~']
选项匹配
\\]
中的第一个
\
,然后将剩余的
\]
作为
转义字符进行匹配。它这样做的原因是,这种解释导致的匹配比
\\
转义字符
]
是范围结束的情况更长,并且当有多种有效方法匹配词法规则时,ANTLR总是选择最长的一种(除非涉及
*?


要解决这个问题,您应该更改
RangeChar
,这样反斜杠只允许作为转义序列的一部分,即用
~[\]\]
替换
~[\]\]

非常感谢。我假设如果出现反斜杠,它会跳过
~']
,因为
转义字符
匹配会更长。@ThomasS。是的,但是最大咀嚼原则适用于lexer规则,而不是片段。也就是说,它使用的路径为
Range
生成尽可能长的匹配,而不是
RangeChar
。非常感谢。我假设如果出现反斜杠,它会跳过
~']
,因为
转义字符
匹配会更长。@ThomasS。是的,但是最大咀嚼原则适用于lexer规则,而不是片段。也就是说,它与为
Range
而不是
RangeChar
生成最长可能匹配的路径相匹配。