【问题标题】:Semicolon in a string causes problems字符串中的分号会导致问题
【发布时间】:2021-12-30 03:31:09
【问题描述】:

在我的 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”。

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

知道我做错了什么吗?

【问题讨论】:

    标签: java antlr4


    【解决方案1】:

    首先:不要在解析器规则中使用文字标记(除非您确切知道它的作用)。 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 不会像我上面那样命名它们,而是将它们命名为 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 )+;
    

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

    【讨论】:

      猜你喜欢
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-25
      • 2019-11-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多