字符串插值的语法,其中格式错误的插值被视为正常字符串

Grammar for string interpolation where malformed interpolations are treated as normal strings

这是我要解析的语言的一个子集:

到目前为止,这是足够直接的。这是有效的方法:

词法分析器:

lexer grammar string_testLexer;

STRING_START: '"' -> pushMode(STRING);
WS: [ \t\r\n]+  -> skip ;
ID: [A-Z]+;
EQ: '=';

mode STRING;

VAR_START: '[' -> pushMode(INTERPOLATION);
DOUBLE_QUOTE_INSIDE: '"' -> popMode;
REGULAR_STRING_INSIDE: ~('"'|'[')+;


mode INTERPOLATION;
ID_INSIDE: [A-Z]+;
CLOSE_BRACKET_INSIDE: ']' -> popMode;

解析器:

parser grammar string_testParser;

options { tokenVocab=string_testLexer; }

mainz: stat *;
stat: ID EQ string;

string: STRING_START string_part* DOUBLE_QUOTE_INSIDE;
string_part: interpolated_var | REGULAR_STRING_INSIDE;
interpolated_var: VAR_START ID_INSIDE CLOSE_BRACKET_INSIDE;

到目前为止一切顺利。但是还有一种语言功能:

例如:

A = "hello" => "hello"
B = "h[A]a" => "h", A, "a"
C="h [A] a" => "h ", A, " a"
D="h [A][V] a" => "h ", A, V, " a"
E = "h [A] [V] a" => "h ", A, " ", V, " a"
F = "h [aVd] a" => "h [aVd] a"
G = "h [Va][VC] a" => "h [Va]", VC, " a"
H = "h [V][][ff[Z]" => "h ", V, "[][ff", Z

我试图将 REGULAR_STRING_INSIDE: ~('"'|'[')+; 替换为 REGULAR_STRING_INSIDE: ~('"')+;,但这在 ANTLR 中不起作用。它导致将上面的所有行匹配为字符串。

因为在 ANTLR4 中没有启用回溯我不确定如何克服这个问题并告诉 ANTLR 如果它不匹配 interpolated_var 规则它应该继续匹配 REGULAR_STRING_INSIDE相反,它似乎总是选择后者。

我读到词法分析器总是匹配最长的标记,所以我尝试将 REGULAR_STRING_INSIDEVAR_START 提升为解析器规则,希望解析器中的替代顺序将得到尊重:

r: REGULAR_STRING_INSIDE
v: VAR_START

string: STRING_START string_part* DOUBLE_QUOTE_INSIDE;
string_part: v ID_INSIDE CLOSE_BRACKET_INSIDE | r;

这似乎没有任何区别。

我还读到 antlr4 semantic predicates 可以提供帮助。但是我很难想出在这种情况下需要应用的那些。

如何修改上面的语法,使其可以匹配两个内插位,或者如果它们格式不正确,则将它们视为字符串?

测试输入:

A = "hello"
B = "h[A]a"
C="h [A] a"
D="h [A][V] a"
E = "h [A] [V] a"
F = "h [aVd] a"
G = "h [Va][VC] a"
H = "h [V][][ff[Z]"

我如何编译/测试:

antlr4 string_testLexer.g4
antlr4 string_testParser.g4
javac *.java
grun string_test mainz st.txt -tree

I tried to replace REGULAR_STRING_INSIDE: ~('"'|'[')+; With just REGULAR_STRING_INSIDE: ~('"')+;, but that does not work in ANTLR. It results in matching all the lines above as strings.

正确,ANTLR 尝试尽可能匹配。所以 ~('"')+ 太贪心了。

I also read that antlr4 semantic predicates could help.

只有在万不得已时才使用谓词。它在您的语法中引入了目标特定代码。如果不需要(在本例中不需要),则不要使用它们。

尝试这样的事情:

REGULAR_STRING_INSIDE
 : ( ~( '"' | '[' )+ 
   | '[' [A-Z]* ~( ']' | [A-Z] ) 
   | '[]'
   )+
 ;

上面的规则将读作:

  1. 匹配 "[ 以外的任何字符一次或多次
  2. OR 匹配 [ 后跟零个或多个大写字母,后跟 ] 以外的任何字符或大写字母(您的 [Va[aVd 情况)
  3. 或匹配一个空块,[]

并匹配以上 3 个备选方案之一或多次以创建单个 REGULAR_STRING_INSIDE.

如果字符串可以以 one 或 mote [ 结尾,您可能还想这样做:

DOUBLE_QUOTE_INSIDE
 : '['* '"' -> popMode
 ;