字符串中的分号会导致问题
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 的词法分析器是如何工作的:
- 它会尝试匹配尽可能多的字符
- 当 2 个或多个词法分析器规则匹配相同数量的字符时,让第一个定义的“赢”
由于规则 2,输入 3
将始终成为 IDENTIFIER
而永远不会成为 VALUE_STRING_CHARACTER
标记。
因此,如果您希望 boitValue
也匹配 IDENTIFIER
规则中的分号和字符,您需要这样做:
boitValue : ( VALUE_STRING_CHARACTER | IDENTIFIER | SEMI )+;
然后它将像这样解析您的示例输入:
我在 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 的词法分析器是如何工作的:
- 它会尝试匹配尽可能多的字符
- 当 2 个或多个词法分析器规则匹配相同数量的字符时,让第一个定义的“赢”
由于规则 2,输入 3
将始终成为 IDENTIFIER
而永远不会成为 VALUE_STRING_CHARACTER
标记。
因此,如果您希望 boitValue
也匹配 IDENTIFIER
规则中的分号和字符,您需要这样做:
boitValue : ( VALUE_STRING_CHARACTER | IDENTIFIER | SEMI )+;
然后它将像这样解析您的示例输入: