前面文章我们学习了编译器前端的词法和语法分析工具,本篇我们来看看如何借助 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 类

把这部分代码放到你的 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进行语法分析。

【编译原理】Antlr 入门使用的更多相关文章

  1. 编译原理---antlr实践+编译过程理解+课程理解知识点

    0.其他说明 0.0编译器分为前.中.后端,课上主要学的是前端.前端又分为词法分析(lexical analysis).语法分析(syntax analysis).语义分析(semantic anal ...

  2. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  3. 跟vczh看实例学编译原理——一:Tinymoe的设计哲学

    自从<序>胡扯了快一个月之后,终于迎来了正片.之所以系列文章叫<看实例学编译原理>,是因为整个系列会通过带大家一步一步实现Tinymoe的过程,来介绍编译原理的一些知识点. 但 ...

  4. Compiler Theory(编译原理)、词法/语法/AST/中间代码优化在Webshell检测上的应用

    catalog . 引论 . 构建一个编译器的相关科学 . 程序设计语言基础 . 一个简单的语法制导翻译器 . 简单表达式的翻译器(源代码示例) . 词法分析 . 生成中间代码 . 词法分析器的实现 ...

  5. Java编译原理

    http://wenku.baidu.com/view/f9b1734b87c24028915fc3a3.html Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想 ...

  6. 编译原理-词法分析05-正则表达式到DFA-01

    编译原理-词法分析05-正则表达式到DFA 要经历 正则表达式 --> NFA --> DFA 的过程. 0. 术语 Thompson构造Thompson Construction 利用ε ...

  7. 跟vczh看实例学编译原理——三:Tinymoe与无歧义语法分析

    文章中引用的代码均来自https://github.com/vczh/tinymoe.   看了前面的三篇文章,大家应该基本对Tinymoe的代码有一个初步的感觉了.在正确分析"print ...

  8. 跟vczh看实例学编译原理——二:实现Tinymoe的词法分析

    文章中引用的代码均来自https://github.com/vczh/tinymoe.   实现Tinymoe的第一步自然是一个词法分析器.词法分析其所作的事情很简单,就是把一份代码分割成若干个tok ...

  9. 跟vczh看实例学编译原理——零:序言

    在<如何设计一门语言>里面,我讲了一些语言方面的东西,还有痛快的喷了一些XX粉什么的.不过单纯讲这个也是很无聊的,所以我开了这个<跟vczh看实例学编译原理>系列,意在科普一些 ...

  10. 编译原理-词法分析04-NFA & 代码实现

    编译原理-词法分析04-NFA & 代码实现 0.术语 NFA 非确定性有穷自动机nondeterministic finite automation. ε-转换ε-transition 是无 ...

随机推荐

  1. 【Azure Redis 缓存】Azure Redis 4.0 被扫描到漏洞,如何修补呢?

    问题描述 在安全级别要求高的公司中,任何系统都会进行安全扫描.比如Azure 云上的Redis服务,也在扫描的范围中,最后发现Redis 4.0存在以下漏洞: CVE-2019-10192:https ...

  2. 【对比】Gemini:听说GPT-4你小子挺厉害

    前言 缘由 谷歌连放大招:Gemini Pro支持中文,Bard学会画画 事情起因: 一心只读圣贤书的狗哥,不经意间被新闻吸引.[谷歌最新人工智能模型Gemini Pro已在欧洲上市 将与ChatGP ...

  3. Java 抽象类 练习

    1 package com.bytezreo.template; 2 3 import java.util.Calendar; 4 import java.util.Scanner; 5 6 /** ...

  4. MES集成警报灯系统,Http远程控制系统设计

    核心功能设计 警报灯实机演示:https://www.bilibili.com/video/BV1294y1M7f3?p=2 接受服务器发送http·post请求远程控制警报灯,可接入MES等系统. ...

  5. vscode 两种定位跳转的方法 ctrl+p 方法1 path:行号 方法2 #变量名 - 针对$store变量不好找的方案 方法1可以备注在代码里面

    vscode 两种定位跳转的方法 ctrl+p 方法1 path:行号 方法2 #变量名 - 针对$store变量不好找的方案 方法1可以备注在代码里面 问题 $store的变量不能跳转,有跳转插件也 ...

  6. [VueJsDev] 快速入门 - vscode 自动格式化

    [VueJsDev] 目录列表 https://www.cnblogs.com/pengchenggang/p/17037320.html vscode 自动格式化(vue) ::: details ...

  7. 单词本z exploration plor,ploit — flow out ,weep

    exploration plor,ploit - flow out ,weep 为什么 今天新学了个单词 exploration 很简单可以查出和 explore有关联 exploration n. ...

  8. gap 单词学习 对标 open

    为什么gap 和 open 联系记忆呢? gap是从行为动作中来 open 中 op 就是 up,是从 单词字母的角度来 但是 本意 这两个单词都差不多 gap gap : 来自PIE*ghai,打呵 ...

  9. C++标准库字符串流sstream

    sstream与strstream 在C++有两种字符串流,一种在<strstream>中定义,另一种在<sstream>中定义,两者的区别如下: strstream里包含st ...

  10. GoFrame 优化接口的错误码和异常的思路

    前言 你是否想在使用 GoFrame 的过程中,拥有一个能打印异常堆栈,能自定义响应状态码,能统一处理响应数据的接口.如果你回答是,那么,请耐心看完本文,或许会对你有所启发.若文中由表达不当之处,恳请 ...