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

时间:2019-01-13 07:05:27

标签: antlr antlr4

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

  • 程序由语句组成
  • 声明是分配:A = "b"
  • 工作分配的左侧是一个标识符(全部大写)
  • 工作分配的右侧是用引号引起来的字符串
  • 字符串通过插入括号括起来的标识符(A = "b[C]d")支持字符串插值

到目前为止,这很简单。这是有效的方法:

词法分析器:

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

1 个答案:

答案 0 :(得分:1)

  

我试图用REGULAR_STRING_INSIDE:〜('“')+;代替REGULAR_STRING_INSIDE:〜('”'|'[')+ ;,但这在ANTLR中不起作用。结果将上面的所有行都匹配为字符串。

正确,ANTLR尝试尽可能匹配。因此~('"')+太贪婪了。

  

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

仅将谓词用作最后的手段。它在语法中引入了目标特定的代码。如果不需要(在这种情况下不需要),请不要使用它们。

尝试这样的事情:

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

上面的规则为:

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

并一次或多次匹配以上3种选择之一,以创建一个REGULAR_STRING_INSIDE

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

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