首页 > 其他分享 >【编译原理】Antlr 入门使用

【编译原理】Antlr 入门使用

时间:2024-04-09 21:11:40浏览次数:26  
标签:ExprParser 入门 antlr Antlr expr 词法 编译 import new

前面文章我们学习了编译器前端的词法和语法分析工具,本篇我们来看看如何借助 Antlr 工具,快速生成词法和语法分析代码。

一、安装

mac 环境:
1)安装

brew install antlr

2)配置 classpath
(把 Antlr 的 JAR 文件设置到 CLASSPATH 环境变量中,以便顺利编译所生成的 Java 源代码。)

vi ~/.bash_profile

# 替换成你的 antlr jar 路径
CLASSPATH=".:/opt/homebrew/Cellar/antlr/4.13.1/antlr-4.13.1-complete.jar:$CLASSPATH"

source ~/.bash_profile

有了这个玩意,你可以用很简单的方式定义好词法和语法文件,他会自动生成对应的解析文件,给你生成出 AST 来。

你可以从生成的类文件中,看看是如何生成 AST 树的。
对于我们之前遇到的左递归问题,它又是如何解决的,也是用循环代替递归么?

生成 AST 树,算完成了词法分析和语法分析。
根据这棵树做什么,就是语义分析了。

二、开发 Java 项目

1、创建一个 maven 项目
2、pom 中添加 Antlr 库

        <dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-runtime</artifactId>
            <version>4.10</version>
        </dependency>

3、编写一个 antlr 文件 Expr.g4。位置随意,可以放到 src 目录

grammar Expr;

expr: expr op=(ADD|SUB) expr      # AddSub
| INT                             # int
;

ADD: '+';
SUB: '-';

INT : [0-9]+ ;
WS : [ \t]+ -> skip;

4、编译项目 (这样可以生成一些antlr的解析器的类代码,方便后面编程)

mvn compile

你应该能在项目根目录看到一个 gen 文件夹,打开后里面是生成的 java 类
image.png

把这部分代码放到你的 src 包路径下 src/main/java/com/xxx/my_antlr_demo/antlr4
5、编写调用代码
EvalVisitor.java

import com.shuofxz.my_antlr_demo.antlr4.ExprBaseVisitor;
import com.shuofxz.my_antlr_demo.antlr4.ExprLexer;
import com.shuofxz.my_antlr_demo.antlr4.ExprParser;

public class EvalVisitor extends ExprBaseVisitor<Integer> {
    @Override
    public Integer visitAddSub(ExprParser.AddSubContext ctx) {
        Integer left = visit(ctx.expr(0));  // should call "visit", not "visitChildren"
        Integer right = visit(ctx.expr(1));
        if (ctx.op.getType() == ExprLexer.ADD) {
            return left + right;
        } else {
            return left - right;
        }
    }

    @Override
    public Integer visitInt(ExprParser.IntContext ctx) {
        return Integer.valueOf(ctx.INT().getText());
    }
}

AppDemo.java

import com.shuofxz.my_antlr_demo.antlr4.ExprLexer;
import com.shuofxz.my_antlr_demo.antlr4.ExprParser;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;

public class AppDemo {
    public static void main(String[] args) {
        String input = null;
        // 此处把输入的参数,直接赋值了
        args = new String[2];
        args[0] = "-input";
        args[1] = "1+2+3-4";
        for (int i=0; i<args.length; i++) {
            if (args[i].equals("-input")) {
                input = args[++i];
            }
        }

        if (input == null) {
            System.out.println("args:  -input <expression>");
            return;
        }

        CodePointCharStream charStream = CharStreams.fromString(input);
        ExprLexer lexer = new ExprLexer(charStream);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExprParser parser = new ExprParser(tokens);
        ParseTree tree = parser.expr();
        EvalVisitor visitor = new EvalVisitor();

        Object result = visitor.visit(tree);
        System.out.println("output=" + result);
    }
}

6、运行就能看到结果了。

你可能会有疑问:
兜了这么一大圈这有啥用呢?

那我们把 Antrl 文件修改一下 Expr.g4
ADDSUB 两个操作符换成其他的符号。

grammar Expr;

expr: expr op=(ADD|SUB) expr      # AddSub
| INT                             # int
;

ADD: '@';
SUB: '#';

INT : [0-9]+ ;
WS : [ \t]+ -> skip;

记得重新执行第四步生成代码并替换。

然后我们可以把输入字符换为 1@2@3#4
你大概猜到了这里就实现了类似操作符重载的功能。
那么后面我们就可以用这个工具,实现我们自己的语法解析工具了。

三、Antlr 中都做了什么?

antlr 语法文件中写的都是啥?

  • 分为两个部分:词法规则和语法规则
  • 词法规则定义了语言的基本词汇元素,即词法单元(Tokens)。它们通常包括标识符、常量、关键字和符号等。通常以大写字母开头,如 ADD、INT 等
  • 语法规则定义了语言的结构,说明了不同词法单元是如何组合起来形成语言结构的。语法规则描述了语句、表达式、声明等高级结构,如 expr。

接下来我们解释一下关键执行步骤中都做了什么事情:

// 将字符串转换为 antlr 能接受的 CodePointCharStream 类型
CodePointCharStream charStream = CharStreams.fromString(input);

// 创建一个词法分析器实例
ExprLexer lexer = new ExprLexer(charStream);
// 创建一个记号流实例
CommonTokenStream tokens = new CommonTokenStream(lexer);
// 创建一个语法分析器实例
ExprParser parser = new ExprParser(tokens);

// 这是实际开始进行词法和语法分析的步骤,生成 AST
ParseTree tree = parser.expr();

// 遍历 AST。按照自己定义的 visitXxx() 方法执行实际的逻辑。
EvalVisitor visitor = new EvalVisitor();
Object result = visitor.visit(tree);
  • 词法分析器:词法分析的任务是将输入文本分割成一系列的记号(tokens),每个记号是语言中最小的有意义单元,如关键字、标识符、字面量等。
  • 记号流:用于从词法分析器中获取记号,并将它们组织成一个流,以便之后进行语法分析。
  • 语法分析器:对记号流tokens进行语法分析。

标签:ExprParser,入门,antlr,Antlr,expr,词法,编译,import,new
From: https://www.cnblogs.com/shuofxz/p/18124826

相关文章

  • MinGW-w64 C/C++编译器的下载和安装
    1.介绍        MinGW(“MinimalistGNUforWindows”),以前称为mingw32,是一个用于创建MicrosoftWindows应用程序的免费开源软件开发环境。        MinGW-w64项目是gcc的完整运行时环境,支持Windows64位和32位操作系统本机二进制文件。2.安装包下载......
  • 第 9 场 小白入门赛 字典树考试
    题目:4.字典树考试【算法赛】-蓝桥云课(lanqiao.cn)思路:我们可以先抛开题目,想一下一个二进制数是111111111 --->9个1,题目说(Ai&Aj)所以两个1一个组合,我们用最笨的方式取枚举----->是8+7+6+5+.......+1是36两两一组,想想X个1如何算呢?是不是应......
  • Qt使用Sqlite数据库-1(入门级)
    1.在Pro文件中加入sql资源QT+=coreguisql    这是第一步也是最重要的一步,没有加入sql资源。在包含数据库文件时会报错找不到该文件。2.创建链接及打开数据库//包含数据库头文件#include<QSqlDatabase>#include<QSqlError>#include<QSqlQuery>//创建链接......
  • 【牛客SQL快速入门】SQL基础(二)
    一、高级查询1.计算函数AVGAVG()为平均值函数,通过对表中行数计数并计算其列值之和,求得该列的平均值。AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。Selectavg(gpa)Fromuser_profileCOUNTCOUNT()函数为计数函数,可利用COUNT() 确定表中行的数......
  • 一个关于编译器优化选项问题的解决
    因为当前项目单片机容量不够使用,打算开启编译器优化,结果在使用KEIL编译器优化后,程序在发送Modbus数据时,程序直接跑飞了先说结论:最后发现是局部变量指针作为了DMA的内存地址参数,导致当DMA连续搬运数据时,实际那个局部变量已经被释放,导致DMA搬运数据的过程中出现错误,但是为什么没优......
  • FPGA入门笔记012——嵌入式块RAM应用之ROM
    1、实验现象​ 实现一组固定的数据(三角波形表)存储在FPGA中使用IP核构建的片上ROM中,开发板上电后,系统开始从ROM中读出数据,并将数据直接通过并口输出。通过使用SignalTapII软件实时抓取并口上的数据,显示得到三角波形。然后使用Quartus软件中提供的In-SystemMemor......
  • 谷芦语(Guruica)入门
    简介谷芦语(Guruica)印度尼西亚爪哇岛的中爪哇省山区的谷芦村(guru)的语言,在主要城市梭罗(Surakarta)的东北部。相传当时第一个荷兰探险者进入谷芦村后,指着一个村民询问这是哪里。村民以为是问他是谁,就说“我吗?”(guruauru?),因此此地就被称为guru了。另一说法是村民对探险者打招呼,说......
  • Qt 项目编译流程总结
    通过VS来开发Qt项目的过程中,整个Qt项目的编译需要如下几个过程:Uic---->Rcc---->Moc---->预处理----->编译----->汇编----->链接对比普通的C++项目,Qt多了Uic---->Rcc---->Moc这三步。现在对QT项目所多出的三个编译过程进行分析:一、Uic:QtUserInte......
  • JavaSE笔记10数组入门
    数组的入门概念数组属于引用数据类型,其父类是Object数组可以容纳多个元素。(数组是一个数据的集合)数组可以存储基本和引用数据类型数组是引用类型,所以存储再堆内存中数组不能直接存储Java对象,但是可以存储其引用(内存地址)分类一维数组二维数组多维数组二维数组本质......
  • vue快速入门(十四)reduce求和
    注释很详细,直接上代码新增内容非嵌套情况求和嵌套情况求和源码<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><ti......