Antlr4:如何更改我的语法以让链式元素被解析?

Antlr4: How to change my grammar to let chained elements being parsed?

我需要一个语法来解析双点分隔的标记,例如:

1..5v[1]..v[2]1+f(1)..2+v[f(2)]..3+f(3).

基本上,这些标记表示整数范围,例如,1..5 表示范围为 1 到 5 的整数。标记文字应仅表示为 "Integer..Integer"

我还必须解析一些整数文字和实数文字。 所以目前,我有一个自下而上的语法,如:

unary_expr
  : range_expr                 # ToRangeExpr
  | PLUS rhs=unary_expr        # UnaryPlusExpr
  | MINUS rhs=unary_expr       # UnaryMinusExpr
  | NOT rhs=unary_expr         # UnaryNotExpr
  ;

range_expr
  : index_expr                             # ToIndexExpr
  | lhs=index_expr RANGEDOT rhs=index_expr # RangeExpr
  | lhs=range_literal rhs=index_expr       # RangeLiteralExpr
  ;

index_expr
  : atom                      # ToAtom
  | atom LBRACK expression RBRACK   # IndexExpr
  ;

atom
  : vector_atom               # ToVectorAtom
  | matrix_atom               # ToMatrixAtom
  | boolean_literal           # ToBooleanLiteral
  | int_literal               # ToIntegerLiteral
  | real_literal              # ToRealLiteral
  | char_literal              # ToCharLiteral
  | string_literal            # ToStringLiteral
  | tuple_literal             # ToTupleLiteral
  | range_literal             # ToRangeLiteral
  | tuple_element             # ToTupleElement
  | type_cast                 # ToTypeCast
  | stream_state              # ToStreamState
  | function_call             # ToFunctionCall
  | ID                        # IDAtom
  | IDENTITY                  # IdentityLiteral
  | NULL                      # NullLiteral
  | LPAREN expression RPAREN  # ToSubExpr


range_literal: RANGE_LITERAL;

RANGE_LITERAL
    : INT_LITERAL RANGEDOT INT_LITERAL
    ;

REAL_LITERAL
    : DOT US+ INT_LITERAL REAL_EXP?
    | INT_LITERAL DOT US* INT_LITERAL? REAL_EXP?
    | INT_LITERAL REAL_EXP
    | DOT INT_LITERAL REAL_EXP
    ;

REAL_EXP
    : 'e' US* (PLUS | MINUS |)? US* INT_LITERAL
    ;

INT_LITERAL: NUM (NUM | US)*;

所以目前,我的语法可以解析多个整数链接的范围标记。但是,我无法解析任何多表达式链接的范围标记。我试图将我的 range_expr 更改为(使其更加模糊):

range_expr
  : range_literal
  | index_expr (RANGEDOT index_expr*)
  ;

但是,它并没有改变我的解析敏感度。那么我还应该做些什么改变让我的语法解析多个 index_expr 链式范围标记?

说明

我无法重用你的语法(因为缺少 lexer/parser 规则)但如果我正确理解问题:你想要一个简单的两个数字范围或将任意数量的数字链接在一起expr。这样做的想法是在 index_expr 中有一个匹配数字范围的子规则(exprChain 的特殊版本),并有一个 expr 的递归定义,它将包含一个链接表达式 (exprChain).

解决方案

作为思路的一个例子我介绍一下小语法。

grammar test;

INT : [0-9]+;
REAL : [0-9]* '.' [0-9]+;
NAME : [a-zA-Z]+;

numeric
    : INT | REAL
    ;

reference
    : NAME              # variable
    | NAME '[' expr ']' # array
    | NAME '(' expr ')' # functionCall
    ;

index_expr
    : numeric '..' numeric  # rangeOfNumbers
    | expr                  # classicExpr
    ;

expr
    : expr '+' expr   # exprAdd
    | reference       # exprRef
    | numeric         # exprNumber
    | expr '..' expr  # exprChain
    ;

此示例语法能够匹配您提到的所有范围表达式:1..5.1...3(如 rangeOfNumbers)、v[1]..v[2]1+f(1)..2+v[f(2)]..3+f(3) (均为 exprChain)。

问题是我无法在我的语法中将 range_expr 视为 unary_expr,因为它会混淆 antlr4 与 real_literal 选项,并强制 antlr4 匹配 logic_expr 标记(即 xoror,我没有在我的问题中显示它们)。在我的微修复之后,部分语法将是这样的:

unary_expr
  : index_expr                 # ToIndexExpr
  | PLUS rhs=unary_expr        # UnaryPlusExpr
  | MINUS rhs=unary_expr       # UnaryMinusExpr
  | NOT rhs=unary_expr         # UnaryNotExpr
  ;

index_expr
  : atom                      # ToAtom
  | atom LBRACK expression RBRACK   # IndexExpr
  | lhs=index_expr RANGEDOT rhs=index_expr  # RangeExpr
  | lhs=range_literal rhs=index_expr # RangeLiteralExpr
  ;

atom
  : vector_atom               # ToVectorAtom
  | matrix_atom               # ToMatrixAtom
  | boolean_literal           # ToBooleanLiteral
  | int_literal               # ToIntegerLiteral
  | real_literal              # ToRealLiteral
  | char_literal              # ToCharLiteral
  | string_literal            # ToStringLiteral
  | tuple_literal             # ToTupleLiteral
  | range_literal             # ToRangeLiteral
  | tuple_element             # ToTupleElement
  | type_cast                 # ToTypeCast
  | stream_state              # ToStreamState
  | function_call             # ToFunctionCall
  | ID                        # IDAtom
  | IDENTITY                  # IdentityLiteral
  | NULL                      # NullLiteral
  | LPAREN expression RPAREN  # ToSubExpr
  ;


real_literal: REAL_LITERAL     # RealLiteral
            | DOT INT_LITERAL  # EdgeCaseRealLiteral;
range_literal: RANGE_LITERAL;


RANGE_LITERAL
    : INT_LITERAL RANGEDOT
    ;

REAL_LITERAL
    : DOT US+ INT_LITERAL REAL_EXP?
    | INT_LITERAL DOT US* INT_LITERAL? REAL_EXP?
    | INT_LITERAL REAL_EXP
    | DOT INT_LITERAL REAL_EXP
    ;

REAL_EXP
    : 'e' US* (PLUS | MINUS |)? US* INT_LITERAL
    ;

INT_LITERAL: NUM (NUM | US)*;