字符串中的分号会导致问题

Semicolon in a string causes problems

我在 ANTLR4 语法中允许在字符串中使用分号时遇到了一些问题。

我的语法应该接受这个:

prop_name@Default:'Building 3;100'

我的语法是这样的:

grammar BoitFilter;

filter : ';'* expression ( ';'+ expression )* ';'*;

expression : field boitOperator boitValueExpression;

field : ( parent '.' )? field_name;

parent : IDENTIFIER;

field_name : IDENTIFIER;

IDENTIFIER : [a-zA-Z0-9_@\[\]\.]+;

boitInOperator : ':(' boitValueExpression ( ','+ boitValueExpression )* ')';

boitOperator : ( ':' | '<' | '>' | '<:' | '>:' | boitInOperator );

boitValueExpression : QUOTE boitValue QUOTE;

boitValue : VALUE_STRING_CHARACTER+;

VALUE_STRING_CHARACTER : [\ \:\;åäöÅÄÖa-zA-Z_0-9\*\-];

QUOTE : '\'';

我认为我的 VALUE_STRING_CHARACTER 语法可能是错误的,但我不确定为什么。

在我的 Java 代码中,我有一个 boitValue 的侦听器:

@Override
public void enterBoitValue(BoitFilterParser.BoitValueContext ctx) {
    String textValue = ctx.getText();
    // Do something with the text
}

在这里,我希望 textValue 变量是“'Building 3;100'”,但它的值是“'Building 3<missing '''>”。

我的语法似乎无法接受分号作为字符串的一部分。

知道我做错了什么吗?

首先:不要在解析器规则中使用文字标记(除非您确切知道它的作用)。你的语法被 ANTLR 解释如下:

grammar BoitFilter;

filter              : SEMI* expression ( SEMI+ expression )* SEMI*;
expression          : field boitOperator boitValueExpression;
field               : ( parent DOT )? field_name;
parent              : IDENTIFIER;
field_name          : IDENTIFIER;
boitInOperator      : COL_O_PAR boitValueExpression ( COMMA+ boitValueExpression )* C_PAR;
boitOperator        : ( COL | LT | GT | LT_COL | GT_COL | boitInOperator );
boitValueExpression : QUOTE boitValue QUOTE;
boitValue           : VALUE_STRING_CHARACTER+;

SEMI                   : ';';
DOT                    : '.';
COL_O_PAR              : ':(';
COMMA                  : ',';
C_PAR                  : ')';
COL                    : ':';
LT                     : '<';
GT                     : '>';
LT_COL                 : '<:';
GT_COL                 : '>:';
IDENTIFIER             : [a-zA-Z0-9_@[\].]+;
VALUE_STRING_CHARACTER : [ :;åäöÅÄÖa-zA-Z_0-9*\-];
QUOTE                  : '\'';

当然,ANTLR 不会像我上面那样命名它们,ANTLR 会把它们命名为T__0...T__10,不过没关系。

如果您现在检查正在为您的输入创建哪些令牌 prop_name@Default:'Building 3;100':

String source = "prop_name@Default:'Building 3;100'";
BoitFilterLexer lexer  = new BoitFilterLexer(CharStreams.fromString(source));
CommonTokenStream stream = new CommonTokenStream(lexer);
stream.fill();

for (Token t : stream.getTokens()) {
  System.out.printf("%-25s'%s'\n", BoitFilterLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
}

您会看到正在打印的内容:

IDENTIFIER               'prop_name@Default'
COL                      ':'
QUOTE                    '''
IDENTIFIER               'Building'
VALUE_STRING_CHARACTER   ' '
IDENTIFIER               '3'
SEMI                     ';'
IDENTIFIER               '100'
QUOTE                    '''
EOF                      '<EOF>'

如您所见,解析器无法从 'Building 3;100' 创建 boitValueExpression。这是因为 ANTLR 的词法分析器是如何工作的:

  1. 它会尝试匹配尽可能多的字符
  2. 当 2 个或多个词法分析器规则匹配相同数量的字符时,让第一个定义的“赢”

由于规则 2,输入 3 将始终成为 IDENTIFIER 而永远不会成为 VALUE_STRING_CHARACTER 标记。

因此,如果您希望 boitValue 也匹配 IDENTIFIER 规则中的分号和字符,您需要这样做:

boitValue : ( VALUE_STRING_CHARACTER | IDENTIFIER | SEMI )+;

然后它将像这样解析您的示例输入: