下面是我想要解析的语言的子集:
- 程序由语句组成
- 语句就是赋值:
A = "b"
- 赋值的左侧是一个标识符(全部大写)
- 赋值的右侧是一个用引号括起来的字符串
- 字符串支持通过插入带括号的标识符(
A = "b[C]d"
)进行字符串插值
到目前为止,这已经足够直接了。以下是有效的:
Lexer:
lexer grammar string_testLexer;
STRING_START: '"' -> pushMode(STRING);
WS: [ trn]+ -> 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
,它似乎总是选择后者。
我读到lexer总是匹配最长的令牌,所以我尝试将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语义谓词可能会有所帮助。但我很难想出在这种情况下需要应用的方法。
我如何修改上面的语法,使其能够匹配两个插值位,或者如果它们格式不正确,则将它们视为字符串
测试输入:
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
我试图用REGULARSTRING_INSIDE:~('"'|'[')+;替换REGULAR_STRING_INSIDE:~(")+;,但这在ANTLR中不起作用。这导致上面的所有行都匹配为字符串。
正确,ANTLR尽量匹配。所以~('"')+
太贪婪了。
我还读到antlr4语义谓词可能会有所帮助。
只能在万不得已的情况下使用谓词。它在语法中引入了特定于目标的代码。如果不需要(在这种情况下是不需要的),那么就不要使用它们。
试试这样的东西:
REGULAR_STRING_INSIDE
: ( ~( '"' | '[' )+
| '[' [A-Z]* ~( ']' | [A-Z] )
| '[]'
)+
;
上面的规则是:
- 匹配
"
或[
以外的任何字符一次或多次 - 或匹配一个
[
,后跟零个或多个大写字母,后跟除]
或大写字母以外的任何字符(您的[Va
和[aVd
情况) - OR匹配空块
[]
并将上述3个备选方案中的一个匹配一次或多次,以创建单个REGULAR_STRING_INSIDE
。
如果一个字符串可以以一个或多个[
结尾,您可能还想这样做:
DOUBLE_QUOTE_INSIDE
: '['* '"' -> popMode
;