Antlr4: 为parser rule添加label

news2024/11/16 22:01:22

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;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/390764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年显卡性能跑分排名表

2022年显卡性能跑分排名表(数据来源于快科技)这个版本的电脑显卡跑分榜第一的是NVIDIA GeForce RTX 3090 Ti显卡。由于显卡跑分受不同的测试环境、不同的显卡驱动版本以及不同散热设计而有所不同,所以显卡跑分会一直变化。 前二十名的台式电…

Linux(进程概念详解)

进程是如今编程领域非常重要的一个概念,进程是比较抽象的,不容易直接理解。因为进程与操作系统息息相关,因此在介绍进程之前,笔者打算先简易讲一下操作系统的工作流程,理解操作系统是如何管理软件和硬件的,…

垃圾收集器和内存分配(第五章)

《实战Java虚拟机:JVM故障诊断与性能优化 (第2版)》 Java 平台,标准版热点虚拟机垃圾回收调优指南 垃圾收集器虽然看起来数量比较多,但其实总体逻辑都是因为硬件环境的升级而演化出来的产品,不同垃圾收集器的产生总体可以划分为几…

智能优化算法应用:基于蚁狮优化算法的工程优化案例-附代码

智能优化算法应用:基于蚁狮算法的工程优化案例 文章目录智能优化算法应用:基于蚁狮算法的工程优化案例1.蚁狮算法2.压力容器设计问题3.三杆桁架设计问题4.拉压弹簧设计问题5.Matlab代码6.python代码摘要:本文介绍利用蚁狮搜索算法&#xff0c…

191、【动态规划】AcWing —— 900. 整数划分:完全背包解法+加减1解法(C++版本)

题目描述 参考文章:900. 整数划分 解题思路 因为本题中规定了数字从大到小,其实也就是不论是1 2 1 4,还是2 1 1 4,都会被看作是2 1 1 4这一种情况,因此本题是在遍历中不考虑结果顺序。 背包问题中只需考虑…

AcWing:并查集

并查集理论基础并查集的作用是什么:将两个集合合并。询问两个元素是否在一个集合当中。如果不使用并查集,要完成上述两个操作,我们需要:创建一个数组来表示某个元素在某个集合之中,如belong[x] a,即x元素在…

0201基础-组件-React

1 组件和模块 1.1 模块 对外提供特定功能的js程序,一般就是一个js文件 为什么拆分模块呢?随着业务逻辑增加,代码越来越多,越来越复杂。作用:复用js,简化js,提高js运行效率 1.2 模块化 当应用…

用gdb.attach()在gdb下断点但没停下的情况及解决办法

在python中,如果导入了pwntools,就可以使用里面的gdb.attach(io)的命令来下断点。 但是这一次鼠鼠遇到了一个情况就是下了断点,但是仍然无法在断点处开始运行,奇奇怪怪。 这是我的攻击脚本 我们运行一下。 可以看到其实已经运行起…

计算机网络模型、协议

ARP(IP->MAC)RARP(MAC->IP)TFTPHTTPDHCPNATARP(IP->MAC) 主机建立自己的ARP缓冲区存ARP列表 广播ARP请求,单播ARP响应 RARP(MAC->IP) 用于无盘工作站&am…

Java分布式全局ID(一)

随着互联网的不断发展,互联网企业的业务在飞速变化,推动着系统架构也在不断地发生变化。 如今微服务技术越来越成熟,很多企业都采用微服务架构来支撑内部及对外的业务,尤其是在高 并发大流量的电商业务场景下,微服务…

[1.#]第一章 计算机系统概述——知识回顾

第一章 计算机系统概述 知识回顾 (对于考研408而言) 这个章节主要以选择题形式考察。 总的来说,这个章节考察的深度、难度不会太大。另外,这个章节的分值占比是比较低的。 不过,对第一章的学习,有助于我们…

使用 Sublime Text 4 优雅地写C++

使用 Sublime Text 4 优雅地写C 进入sublime官网下载sublime的安装包(当然也可以在官网下载页面下载portable版本,不过建议下载默认的setup版本) 双击安装包: 应该一会就下载完成了。 此时可以在应用列表看到sublime:…

谈谈 爬虫遇到的 Access denied Error code 1020

这几天在练习爬虫的时候,遇到一个问题, 通过 python 代码从站点中拿到了目标图片的 url , 但是,在持久化到本地时,出现了错误,所有保存下来的图片都报错:文件损坏, 而且,…

【博学谷学习记录】超强总结,用心分享|狂野大数据课程【DataFrame的相关API】的总结分析

操作dataFrame一般有二种操作的方式, 一种为SQL方式, 另一种为DSL方式 SQL方式: 通过编写SQL语句完成统计分析操作DSL方式: 领域特定语言 指的通过DF的特有API完成计算操作(通过代码形式)从使用角度来说: SQL可能更加的方便一些, 当适应了DSL写法后, 你会发现DSL要比SQL更加…

LeetCode:最长回文子串(动态规划)

一、题目 https://leetcode.cn/problems/longest-palindromic-substring/description/ 二、 算法思想 使用动态规划思想解决,如果一个子串是回文的,并且它的左右两边各加上一个字符后仍然是回文的,那么这个子串加上这两个字符后也一定是回文…

浅谈 TCP 握手/数据传输/挥手过程以及 tcpdump 抓包工具使用

前言浅谈 OSITCP三次握手数据传输四次挥手Socket 服务端/客户端通信测试服务端代码客户端代码tcpdump 命令监控命令总结FAQ怎么确认数据包的大小?TCP 拥塞如何避免?如何理解 TCP keep-alive 原理?总结前言 在网络知识体系,TCP 这块的三次握…

【计算机组成原理】指令系统

目录 指令格式 按指令数目分类: 零地址指令 一地址指令 二地址指令 三地址指令 四地址指令 按指令长度分类: 指令字长 机器字长 存储字长 按操作码的长度分类 定长操作码 可变长操作码 定长指令字结构可变长操作码------>拓展操作码指令…

女子举重问题

一、问题的描述 问题及要求 1、搜集各个级别世界女子举重比赛的实际数据。分别建立女子举重比赛总成绩的线性模型、幂函数模型、幂函数改进模型,并最终建立总冠军评选模型。 应用以上模型对最近举行的一届奥运会女子举重比赛总成绩进行排名,并对模型及…

Java分布式事务(二)

文章目录🔥分布式事务处理_认识本地事务🔥关系型数据库事务基础_并发事务带来的问题🔥关系型数据库事务基础_MySQL事务隔离级别🔥MySQL事务隔离级别_模拟异常发生之脏读🔥MySQL事务隔离级别_模拟异常发生之不可重复读&…

信息安全与数学基础-笔记-②同余

知识目录同余完全剩余系剩余类完全剩余系❀简化剩余系❀欧拉函数逆元!欧拉定理 !同余 a,b 两个数字,都模m,当两个数字模m后余的数一样即为同余。 例子: a bq r (mod m),这里的a 和 r 就是同余 &#xff…