如何使用antlr4编写一个更好的解析器,可以区分属性访问表达式、方法调用表达式、数组访问表达式?

How to use antlr4 write a better parser that can distinguish attribute access expression, method invoke expression, array access expression?

我想用antlr4写一个表达式引擎。

语法如下

expression
:   primary
|   expression '.' Identifier
|   expression '(' expressionList? ')'
|   expression '[' expression ']'
|   expression ('++' | '--')
|   ('+'|'-'|'++'|'--') expression
|   ('~'|'!') expression
|   expression ('*'|'/'|'%') expression
|   expression ('+'|'-') expression
|   expression ('<' '<' | '>' '>' '>' | '>' '>') expression
|   expression ('<=' | '>=' | '>' | '<') expression
|   expression ('==' | '!=') expression
|   expression '&' expression
|   expression '^' expression
|   expression '|' expression
|   expression '&&' expression
|   expression '||' expression
|   expression '?' expression ':' expression
|   <assoc=right> expression
    (   '='
    |   '+='
    |   '-='
    |   '*='
    |   '/='
    |   '&='
    |   '|='
    |   '^='
    |   '>>='
    |   '>>>='
    |   '<<='
    |   '%='
    )
    expression
;

这个语法是对的,但是不能区分属性访问表达式、方法调用表达式和数组访问表达式。所以我把语法改成了

   attributeAccessMethod:    
        expression '.' Identifier;

    expression
        :   primary
        |   attributeAccessMethod
        |   expression '(' expressionList? ')'
        |   expression '[' expression ']'
        |   expression ('++' | '--')
        |   ('+'|'-'|'++'|'--') expression
        |   ('~'|'!') expression

但是这个语法是 左递归 [expression, attributeAccessMethod]。 我怎样才能写出更好的语法 - 我能以某种方式删除左递归 属性并区分这些条件?

为您的不同规则选项添加标签,例如:

expression
:   primary                                 # RulePrimary
|   expression '.' Identifier               # RuleAttribute
|   expression '(' expressionList? ')'      # RuleExpression
|   expression '[' expression ']'           # RuleArray
... etc.

当您针对此规则中的所有 选项执行此操作时,将为这些特殊情况生成您的 BaseVisitor 或 BaseListener public 覆盖,您可以在其中处理每一个你认为合适的。

我不建议你这样定义你的语法。除了@JLH 的回答之外,您的语法还有可能搞乱这些表达式的关联性。

我的建议是,您应该 "top-down" 您的语法具有关联顺序。

例如,您可以在语法中将所有文字、方法调用等视为原子(因为它们总是以文字或标识符开头),并将这些原子与关联运算符相关联。

然后你可以这样写你的语法:

expression: binary_expr;

// Binary_Expr

// Logic_Expr

// Add_expr

// Mult_expr

// Pow_expr

// Unary_expr

associate_expr
: index_expr                            # ToIndexExpr
| lhs=index_expr '.' rhs=associate_expr # AssociateExpr
;

index_expr
: index_expr '[' (expression (COMMA expression)*) ']' # IndexExpr
| atom #ToAtom
;

atom
: literals_1 #wwLiteral
| ...        #xxLiteral
| ...        #yyLiteral
| literals_n #zzLiteral
| function_call # FunctionCall
;


function_call
: ID '(' (expression (',' expression)*)? ')';

// Define Literals
// Literals Block

你的部分算术表达式可能如下所示:

add_expr
: mul_expr                   # ToMulExpr
| lhs=add_expr PLUS rhs=mul_expr  #AddExpr
| lhs=add_expr MINUS rhs=mul_expr #SubtractExpr
;

mul_expr
: pow_expr                         # ToPowExpr
| lhs=mul_expr '+' rhs=pow_expr   # MultiplyExpr
| lhs=mul_expr '/' rhs=pow_expr # DivideExpr
| lhs=mul_expr '%' rhs=pow_expr    # ModExpr
;

您将左侧作为当前表达式,将右侧作为其他级别关联的表达式,这样您可以在对它们进行左递归的同时保持关联性的顺序。