Antlr4: 为parser rule添加label
创始人
2024-05-29 04:41:58
0

1. parser rule中的label

1.1 简介

  • Antrl4语法文件Calculator.g4,stat和expr两个parser rule含有多个rule element,我们这两个parse rule的每个rule element添加了Alternative labels(简称label)
  • 按照Antlr4的语法规则:
    • label一般位于每个rule element的行尾,以#开头。
    • 同时,要么为parser rule的每个rule element添加label,要么一个都不添加
      // 定义stat,不添加label
      stat: expr | ID '=' expr ;// 定义expre,每个rule element都添加label
      expr: expr op=(MUL|DIV) expr| expr op=(ADD|SUB) expr | INT | ID | '(' expr ')' ;
      

1.2 label对parse tree的影响

  • 使用IDEA的Antlr4插件,测试语法规则prog
    • 为stat的rule element添加label后,解析出的stat将使用label进行标识
    • 去除rule element的label后,只能使用序号进行标识
  • 可以说,使用label标识rule element,可以为语法解析带来意想不到的便利

1.3 label的作用

  • Antlr4官网是这样介绍label的:
    1. Labeling Rule Alternatives for Precise Event Methods, we can get more precise parse-tree listener events by labeling the outermost alternatives of a rule using the # operator.
    2. All alternatives within a rule must be labeled, or none of them. Here are two rules with labeled alternatives.
    3. Alternative labels do not have to be at the end of the line and there does not have to be a space after the # symbol. ANTLR generates a rule context class definition for each label.

总结起来如下:

  1. 添加label,可以为每个rule element生成对应的ParserRuleContext,从而快速访问各rule element

    • 由于没有给stat的rule element添加label,只能通过CalculatorParser.StatContext的getter方法获取rule element
      public static class StatContext extends ParserRuleContext {// getter方法public ExprContext expr() {return getRuleContext(ExprContext.class,0);}public TerminalNode ID() { return getToken(CalculatorNoLabelParser.ID, 0); }... // 其他代码省略
      }
      
    • 添加了label,将基于StatContext创建更加具体的Context,这有利于访问parse tree中的各节点
      public static class StatContext extends ParserRuleContext {public StatContext(ParserRuleContext parent, int invokingState) {super(parent, invokingState);}@Override public int getRuleIndex() { return RULE_stat; }public StatContext() { }public void copyFrom(StatContext ctx) {super.copyFrom(ctx);}
      }
      public static class AssignContext extends StatContext {public TerminalNode ID() { return getToken(CalculatorParser.ID, 0); }public ExprContext expr() {return getRuleContext(ExprContext.class,0);}... // 其他代码省略
      }
      public static class PrintExprContext extends StatContext {public ExprContext expr() {return getRuleContext(ExprContext.class,0);... // 其他代码省略
      }
      
  2. 同时,CalculatorListener接口中的监听器方法也更加丰富,以便更加精确地监听parse tree中的节点访问事件

    // 未添加label,只能监听stat节点。具体是赋值节点还是打印节点,需要在代码中区分
    void enterStat(CalculatorNoLabelParser.StatContext ctx);
    void exitStat(CalculatorNoLabelParser.StatContext ctx);// 添加label后的监听器方法更加有针对性
    void enterPrintExpr(CalculatorParser.PrintExprContext ctx);
    void exitPrintExpr(CalculatorParser.PrintExprContext ctx);
    void enterAssign(CalculatorParser.AssignContext ctx);
    void exitAssign(CalculatorParser.AssignContext ctx);
    
  3. 自己的补充: CalculatorVisitor接口中的visitXxx()方法也将变得更加丰富,以便更加精确地访问rule element,从而访问parse tree中的各节点

    // 未给添加label,只能访问stat节点。具体是赋值节点还是打印节点,需要在代码中区分
    T visitStat(CalculatorNoLabelParser.StatContext ctx);// 添加label后,visitStat()方法被以下有针对性的方法替代
    T visitPrintExpr(CalculatorParser.PrintExprContext ctx);
    T visitAssign(CalculatorParser.AssignContext ctx);
    

2. 小建议

  • 建议: 为rule element都加上label,这样获取具体的节点将会更加方便

  • 以visitor模式实现计算器为例,若不为stat的rule element添加label,在CalculatorVisitorImpl中重写CalculatorVisitor.visitStat()方法时,需要自主判断节点的类型

    @Override
    public Integer visitStat(CalculatorNoLabelParser.StatContext ctx) {if (ctx.ID() != null) { // 存在ID说明是赋值语句String variable = ctx.ID().getText();Integer value = visit(ctx.expr());variables.put(variable, value);} else { // 否则是打印语句if (ctx.expr() instanceof CalculatorNoLabelParser.ConstantContext) {System.out.printf("%d\n", visit(ctx.expr()));} else {System.out.printf("%s = %d\n", ctx.expr().getText(), visit(ctx.expr()));}}return 0; // 打印语句统一返回0
    }
    
  • 如果定义了label,无需自主判断节点的类型,可以直接访问具体的节点

    @Override
    public Integer visitPrintExpr(CalculatorParser.PrintExprContext ctx) {Integer result = visit(ctx.expr());if (ctx.expr() instanceof CalculatorParser.ConstantContext) {System.out.println("打印常量的值: " +result);} else {System.out.printf("打印计算结果: %s = %d\n", ctx.expr().getText(), result);}return result;
    }@Override
    public Integer visitAssign(CalculatorParser.AssignContext ctx) {String variable = ctx.ID().getText();Integer value = visit(ctx.expr());variables.put(variable, value);return value;
    }
    

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...