Antlr4 ANTLR中带有贪婪通配符和转义字符的字符串词汇规则

Antlr4 ANTLR中带有贪婪通配符和转义字符的字符串词汇规则,antlr4,Antlr4,摘自《最终ANTLR 4参考》一书: 我们的字符串规则还不够好,因为它不允许 字符串中的双引号。为了支持这一点,大多数语言定义 以反斜杠开始的转义序列。获得双重报价 在双引号字符串中,我们使用\“。来支持公共转义 角色,我们需要如下内容: ​ ANTLR本身需要转义转义字符,所以我们需要\\来 指定反斜杠字符。字符串中的循环现在匹配 通过调用片段规则ESC,或任何 通过点通配符的单个字符。*?子规则运算符 终止(ESC |.*)? 这听起来不错,但当我读到这篇文章时,我注意到在ESC和之间的选择

摘自《最终ANTLR 4参考》一书:

我们的字符串规则还不够好,因为它不允许 字符串中的双引号。为了支持这一点,大多数语言定义 以反斜杠开始的转义序列。获得双重报价 在双引号字符串中,我们使用
\“
。来支持公共转义 角色,我们需要如下内容:

​ ANTLR本身需要转义转义字符,所以我们需要
\\
来 指定反斜杠字符。字符串中的循环现在匹配 通过调用片段规则
ESC
,或任何 通过点通配符的单个字符。
*?
子规则运算符 终止
(ESC |.*)?

这听起来不错,但当我读到这篇文章时,我注意到在
ESC
之间的选择存在某种模糊性。就
字符串而言,通过将转义字符
\
匹配,可以匹配输入
“Hi\”
,并考虑下面的双引号作为关闭字符串。这甚至会减少贪婪,因此更符合
的使用

当然,问题是,如果我们这样做,那么我们在最后会有一个额外的双引号,与任何东西都不匹配

所以我写了以下语法:

grammar String;

anything: STRING '"'? '\r\n';

STRING: '"' (ESC|.)*? '"';
fragment
ESC: '\\"' | '\\\\';
它在字符串后面接受可选的双引号字符。此语法仍将
“Orange\”
解析为完整字符串:


所以我的问题是:为什么这是公认的解析,而不是将
“Orange\”
作为
字符串,后跟一个独立的双引号
?请注意,后者的贪婪程度较低,这似乎更符合
的用法,因此人们可能会认为它更可取。

经过一些实验后,我意识到解释是选择运算符
|
是顺序相关的(但仅在非贪婪运算符
下):
ESC
之前尝试过。如果我将两者颠倒并写入
(.| ESC)*?
,我会得到


这并不令人惊讶,但有趣的是,ANTLR并不像我们有时所期望的那样具有声明性(在某种意义上,逻辑或是顺序独立的,但不是顺序独立的)。这也是一个很好的提醒,非贪婪运算符
并没有将其最小化功能扩展到所有选项,而只是扩展到与输入匹配的第一个选项(@sepp2k添加了顺序依赖性,仅适用于非贪婪情况)。

请注意,只有在使用
*?
(非贪婪)时,顺序才起作用。当不使用
*?
时,最大芒克规则的应用与备选方案的顺序无关。顺便说一句:您可以通过使用
(~'“''ESC)*
(ESC | `')*
来摆脱这里的不贪婪。
grammar String;

anything: STRING '"'? '\r\n';

STRING: '"' (ESC|.)*? '"';
fragment
ESC: '\\"' | '\\\\';