使用golang+antlr4构建一个自己的语言解析器(二)
Antlr4文件解析流程

该图展示了一个语言应用程序中的基本流动过程
- 输入一个字符流,首先经过词法分析,获取各个Token
- 然后经过语法分析,组成语法分析树
Antlr4语法书写规范
语法关键字和使用
| 符号 | 作用 |
|---|---|
| ? | 表达式可选 |
| * | 表达式出现0此或多次 |
| + | 表达式至少一次 |
| EOF | 语法结尾 |
| expr expr1 expr2 | 序列模式,由多个表达式或Token组成一个语法规则 |
| expr|expr1|expr2 | 选择模式,指定一个语法规则可以选择多个匹配模式 |
| expr|expr1|expr* | 嵌套模式,自身引用自身 |
处理优先级、左递归和结合性
Antlr4默认使用自上而下,默认左递归的方式识别语法, 使用下面一个例子说明左递归的方式
expr:expr '*' expr
|expr '+' expr
|INT
;
输入1+2*3;识别的树为

这是因为首先定义了乘法语法规则,然后定义了加法语法规则。
更改左递归的方式
expr:<assoc=right> expr '^' expr
|INT
;
指定第一个expr接受expr的结果作为结合一个语法规则,输入12^3,识别的树为

Antlr4的基本命令我们就了解到这,有兴趣研究的小伙伴,可以查看《Antlr4权威指南》这本书。
第一个脚本
避坑指南
使用go mode模式引用antlr4默认是1.X版本,这个是低版本antlr,无法识别最新的antlr语法,使用
go get -u github.com/antlr/antlr4/runtime/Go/antlr/v4
获取antlr4最新版本的golang包
语法文件
我这里使用的IDE是goland,这个大家根据自己的爱好自选就行。
新建文件夹:parser
在parser文件夹下新建文件:Calc.g4
输入一下内容:
grammar Calc;
//Token
MUL: '*';
DIV: '/';
ADD:+;
SUB-';
NUMBER' | [1-9] ('_'? [0-9])*);
WS_NLSEMI:[ \r\n\t]+ -> skip;
//Rule
expression:
expression op = (MUL | DIV) expression #MulDiv
| expression op = (ADD | SUB) expression #AddSub
| NUMBER
start:expression EOF
- g4文件是Antlr4的文件类型
- Token代表定制的语法中的关键字,Token都是全部大写
- Rule是语法规则,由驼峰样式书写,首字母小写,后续每个单词首字母大写
- EOF代表结尾
脚本命令
我们需要使用Antlr4命令生成Go语言的语法树和Listen方式的go文件
$ java -jar 'C:\Program Files\Java\antlr\antlr-4.12.0-complete.jar' -Dlanguage=Go -no-visitor -package parser *.g4
上述命令就是指定使用antlr4将文件目录下所有的.g4文件生成目标语言为Go的语言文件。
执行CMD命令:
cd .\parser\
执行上述命令,我们会在parser文件夹中看到生成了很多文件:
Calc.interp
Calc.tokens
calc_base_listener.go //监听模式基类的文件
calc_lexer.go //文法文件
calc_listener.go //监听模式的文件(定义多少个语法或者自定义类型就会有多少对Enter、Exit方法)
calc_parser.go //识别语法的文件
验证语法
func main(){
is := antlr.NewInputStream("1+1*2")`
lexer := parser.NewCalcLexer(is)
// Read all tokens
for {
t := lexer.NextToken()
if t.GetTokenType() == antlr.TokenEOF {
break
}
fmt.Printf("%s (%q)\n",lexer.SymbolicNames[t.GetTokenType()], t.GetText())
}
}
输入内容:
NUMBER ("1")
ADD ("+")
NUMBER ("1")
MUL ("*")
NUMBER ("2")
证明我们的每个Token都被识别
建立一个四则运算法则
type calcListener struct {
*parser.BaseCalcListener
stack []int
}
func (l *calcListener) push(i int) {
l.stack = append(l.stack, i)
}
func (l *calcListener) pop() int {
if len(l.stack) < 1 {
panic("stack is empty unable to pop")
}
// Get the last value from the stack.
result := l.stack[len(l.stack)-1]
// Remove the last element from the stack.
l.stack = l.stack[:len(l.stack)-1]
return result
}
func (l *calcListener) ExitMulDiv(c *parser.MulDivContext) {
right, left := l.pop(), l.pop()
switch c.GetOp().GetTokenType() {
case parser.CalcParserMUL:
l.push(left * right)
case parser.CalcParserDIV:
l.push(left / right)
default:
panic(fmt.Sprintf("unexpected op: %s", c.GetOp().GetText()))
}
}
func (l *calcListener) ExitAddSub(c *parser.AddSubContext) {
right, left := l.pop(), l.pop()
switch c.GetOp().GetTokenType() {
case parser.CalcParserADD:
l.push(left + right)
case parser.CalcParserSUB:
l.push(left - right)
default:
panic(fmt.Sprintf("unexpected op: %s", c.GetOp().GetText()))
}
}
func (l *calcListener) ExitNumber(c *parser.NumberContext) {
i, err := strconv.Atoi(c.GetText())
if err != nil {
panic(err.Error())
}
l.push(i)
}
func calc(input string) int {
// Setup the input
is := antlr.NewInputStream(input)
// Create the Lexer
lexer := parser.NewCalcLexer(is)
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
// Create the Parser
p := parser.NewCalcParser(stream)
// Finally parse the expression (by walking the tree)
var listener calcListener
antlr.ParseTreeWalkerDefault.Walk(&listener, p.Start())
return listener.pop()
}
func main(){
fmt.Println(calc(1+1*2))
}
至此,我们已经使用antlr4+golang开始自己第一个语法文件使用,接下来就是如何实现我们自定的语法了!!!
使用golang+antlr4构建一个自己的语言解析器(二)的更多相关文章
- 自己动手写一个编译器Tiny语言解析器实现
然后,上一篇文章简介Tiny词法分析,实现语言.本文将介绍Tiny的语法分析器的实现. 1 Tiny语言的语法 下图是Tiny在BNF中的文法. 文法的定义能够看出.INNY语言有以下特点: 1 程序 ...
- atitit.java解析sql语言解析器解释器的实现
atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...
- 自己动手实现一个简单的JSON解析器
1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...
- 一个简单的json解析器
实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...
- 用c#自己实现一个简单的JSON解析器
一.JSON格式介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着很多优点.例如易读性更好,占用空间更 ...
- VS Extension+NVelocity系列(一)——构建一个简单的NVelocity解析环境
一.前言 本节我们将实际实现一个简单的NVelocity解析环境,以便为以后的实例做一些基本工作,虽然NVelocity如何使用已经属于老掉牙的话题,但我只能专门挑出来一章来做铺垫.人生就是这样无奈啊 ...
- 用c#写一个json的万能解析器
CommonJsonModel .cs /// <summary> /// 万能JSON解析器 /// </summary> public class CommonJsonMo ...
- WCF学习——构建一个简单的WCF应用(二)
我们接着上一篇文章进行讲解 http://www.cnblogs.com/songjianhui/p/7060698.html 一:客户端通过添加引用调用服务 WCF应用服务被成功寄宿后,WCF服务应 ...
- kotlin 写的一个简单 sql 查询解析器
package com.dx.efuwu.core import org.apache.commons.lang.StringUtils import java.sql.PreparedStateme ...
- 从零构建一个简单的 Python Web框架
为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...
随机推荐
- Django启动时提示ModuleNotFoundError: No module named 'xxx'的原因
①创建项目:django-admin startproject DL ②创建app:python manage.py startapp myapp ③启动服务:python manage.py run ...
- logic and control
logic and control 程序的本质 Programs = Data Structures + Algorithms Algorithm = Logic + Control CPU 的构成 ...
- 浏览器输入URL发生了什么:DNS解析、TCP握手、HTTP缓存、重定向、服务器状态码、渲染引擎和JS引擎互斥、渲染过程、浏览器进程、网络进程、渲染进程
输入地址,浏览器查找域名的 IP 地址. 浏览器向 该 IP 地址的web 服务器发送一个 HTTP 请求, 在发送请求之前浏览器和服务器建立TCP的三次握手,判断是否是HTTP缓存, 如果是强制缓存 ...
- 西瓜书3.4 解题报告(python 多分类学习 十折交叉法)
偷懒找了UCI上最小的一个数据集,数据大约是集装箱起重机的转动速度.角度,判断其力量大小(我不懂起重机啊啊啊) 虽然不懂但并不妨碍写代码分类,显然标记就是力量,分为0.3.0.5.0.7三种.具体的模 ...
- simis报错总结
--笔记开始: 1.在前台模块处理时,[单位应收核定]比[人员缴费信息]的在职人员多一人,但是总金额一样,可能是以下原因造成!!! A.从后台看,若正常核定在职的ab08比ac13多一个人,可能是ac ...
- layui 点击显示与点击隐藏
主要有lay-filter属性,靠这个属性监听 <div class="layui-col-xs12 layui-col-sm4 layui-col-md4"> < ...
- 阿里云Linux服务器部署JDK8实战教程
下载地址 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 文件上传 把下载的文 ...
- XML_DTD_20200415
<!-- xml的注释写法 --> 格式良好的xml语言必须具备的几个条件 1.必须有xml声明语句,声明版本号与编码字符集 2.必须有且仅有一个根元素 3.标签大小写敏感 4.属性值 ...
- Vue3 向window注入方法 TS警告 元素隐式具有 "any" 类型,因为索引表达式的类型不为 "number" 问题解决。
window['funcName'] = function(){}; // 'funcName'会标红警告 (window as any).funcName = function(){}; // 正确 ...
- day48-Mysql安装文件结构及SQL常用语句
1.安装文件结构 bin--mysql.exe 客户端运行程序: mysqld.exe 服务端运行程序: data--数据库.数据表等文件 注:修改配置文件后需要重启服务端 2.常用SQL语句 1) ...