这是我要解析的语言的子集:
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_INSIDE
和VAR_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
答案 0 :(得分:1)
我试图用REGULAR_STRING_INSIDE:〜('“')+;代替REGULAR_STRING_INSIDE:〜('”'|'[')+ ;,但这在ANTLR中不起作用。结果将上面的所有行都匹配为字符串。
正确,ANTLR尝试尽可能匹配。因此~('"')+
太贪婪了。
我还读到antlr4语义谓词可能会有所帮助。
仅将谓词用作最后的手段。它在语法中引入了目标特定的代码。如果不需要(在这种情况下不需要),请不要使用它们。
尝试这样的事情:
REGULAR_STRING_INSIDE
: ( ~( '"' | '[' )+
| '[' [A-Z]* ~( ']' | [A-Z] )
| '[]'
)+
;
上面的规则为:
"
或[
以外的任何字符[
,后跟零个或多个大写字母,后跟]
或大写字母以外的任何字符(您的[Va
和[aVd
情况)[]
并一次或多次匹配以上3种选择之一,以创建一个REGULAR_STRING_INSIDE
。
如果字符串可以以一个或一个[
结尾,则您可能还想这样做:
DOUBLE_QUOTE_INSIDE
: '['* '"' -> popMode
;