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

Antlr 字符串插值语法,其中格式错误的插值被视为正常字符串,antlr,antlr4,Antlr,Antlr4,下面是我要分析的语言的子集: 程序由语句组成 语句就是赋值:A=“b” 作业的左侧是一个标识符(所有大写) 赋值的右边是一个用引号括起来的字符串 字符串通过插入括号内的标识符来支持字符串插值(A=“b[C]d”) 到目前为止,这已经足够直截了当了。以下是有效的方法: Lexer: lexer grammar string_testLexer; STRING_START: '"' -> pushMode(STRING); WS: [ \t\r\n]+ -> skip ; ID:

下面是我要分析的语言的子集:

  • 程序由语句组成
  • 语句就是赋值:
    A=“b”
  • 作业的左侧是一个标识符(所有大写)
  • 赋值的右边是一个用引号括起来的字符串
  • 字符串通过插入括号内的标识符来支持字符串插值(
    A=“b[C]d”
到目前为止,这已经足够直截了当了。以下是有效的方法:

Lexer:

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;
到目前为止还不错。但是,还有一个语言特性:

  • 如果括号中没有有效标识符(即所有大写),则将其视为普通字符串
例如:

我试图用
普通字符串替换
普通字符串:~(““”)+;
,但这在ANTLR中不起作用。它会将上面的所有行作为字符串进行匹配

由于在ANTLR4中没有回溯功能,因此我不确定如何克服这一问题,并告诉ANTLR,如果它不匹配
插值的\u var
规则,它应该继续匹配
内部的
常规的\u字符串,\u,相反,它似乎总是选择后者

我读到lexer总是匹配最长的标记,所以我尝试将
REGULAR\u STRING\u INSIDE
VAR\u 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可能会有所帮助,但我很难想出在这种情况下需要应用的方法

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

测试输入:

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
我尝试将内部的规则字符串替换为内部的规则字符串,但在ANTLR中不起作用。它会将上面的所有行作为字符串进行匹配

正确,ANTLR会尽可能多地匹配。因此
~(“”)+
将过于贪婪

我还读到antlr4语义谓词可能会有所帮助

只能使用谓词作为最后手段。它会在语法中引入特定于目标的代码。如果不需要(在本例中不是),则不要使用它们

试着这样做:

REGULAR_STRING_INSIDE
 : ( ~( '"' | '[' )+ 
   | '[' [A-Z]* ~( ']' | [A-Z] ) 
   | '[]'
   )+
 ;
上述规则应为:

  • 一次或多次匹配除
    [
    以外的任何字符
  • 或者匹配
    [
    后跟零个或多个大写字母,后跟除
    ]
    以外的任何字符或大写字母(您的
    [Va
    [aVd
    案例)
  • 或者匹配一个空块,
    []
  • 并将上述3个备选方案中的一个匹配一次或多次,以在
    中创建一个
    常规字符串

    如果字符串可以以一个或多个字符结尾,您也可以这样做:

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

    非常感谢。就你而言,这是写这段语法的最佳方式,还是仅仅是对所呈现的内容进行了最少的修改?我这么问是因为如果我不知道这些规则背后的意图,我会发现它们很难理解,所以自然地,我想知道是否有可能用更多的成语来表达还有,我非常希望能有一个简短的解释,解释一下为什么我的尝试没有奏效,以及为什么你的建议有效(而且确实有效!)。这将有助于我(和其他人)用ANTLR编写更好的语法。
    DOUBLE_QUOTE_INSIDE
     : '['* '"' -> popMode
     ;