使用golang+antlr4构建一个自己的语言解析器(完结篇)
Goland 中Antlr4插件
在goland中安装Antlr4插件,用于识别输入的字符在在语法文件中生成的语法树的样子,大概就是如下的摸样

下载步骤:
1.点击文件中的设置选项

2.在插件目录下输入Antlr4搜索插件

3.点击安装即可
编写自己的语言语法文件
编写语法之前,我们首先要构思一下自己的DSL都有什么关键字,这是个重要的步骤,就像我们学习java或者Golang一样,首先知道这个语法里面都有那些关键字。
我定义的DSL中有四则运算,有比较运算,还有逻辑运算,变量可以有数字、字符串、时间格式
关键字
四则运算
| 作用 | 符号 |
|---|---|
| 乘法 | * |
| 除法 | / |
| 加法 | + |
| 减法 | - |
比较运算
| 作用 | 符号 |
|---|---|
| 等于 | = |
| 不等于 | <> |
| 大于 | > |
| 大于等于 | >= |
| 小于 | < |
| 小于等于 | <= |
逻辑运算
| 作用 | 符号 |
|---|---|
| 且 | && |
| 或 | || |
| 如果 | if |
| 否则 | else |
变量
| 作用 | 符号 |
|---|---|
| 数字 | ('0' | [1-9] ('_'? [0-9])*) |
| 文本 | [\p{Nd}] |
| 字符串 | LETTER (LETTER |
| 日期 | ([0-9]{4}/[0-1]{0,1}[0-9]/[0-3]{0,1}[0-9]) |
常量辅助符号
| 作用 | 符号 |
|---|---|
| 点 | . |
| 逗号 | , |
| 左括号 | ( |
| 右括号 | ) |
| 分号 | ; |
| 多行注释 | '/*' .*? '*/' |
上述就是我们的Token列表,可以创建一个文法文件单独写Token,文法文件申明:lexer grammar Lexer;
编写语法
编写语法之前,我们需要构思一下,我们的DSL可以支持那些语法操作,例如四则运算可以支持字符串运算吗?日期支持四则运算吗?我们可以从基础开始编写,例如我们把变量使用算则模式编写成一个语法规则,
simpleStmt:NUMBER|TEXT|STRING|DATE
我的DSL中支持任何数据的四则运算,那么我就可以使用simpleStmt和四则运算符号组成四则运算
expression:
simpleStmt #SimpleExpression
|expression op = (MUL|DIV) expression #MulDiv
|expression op = (ADD|SUB) expression #AddSub
;
这时候我们就定义了支持四则运算的语法规则,我们来试一下语法定义的对不对。

发现我们输入的加法运算和乘法运算都可以被解析,说明我们的语法定义正确。接下来我们添加比较运算
我定义的DLS支持所有数据做比较运算,那么我直接在上面的expression中添加比较运算就可以了,这里需要注意的是比较运算如果希望有优先级,需要先定义优先级高的比较符号,我这里没有优先级操作,所以都是平级的。
添加比较运算符号
expression:
simpleStmt #SimpleExpression
|expression op = (MUL|DIV) expression #MulDiv
|expression op = (ADD|SUB) expression #AddSub
|expression op = (EQ|NE|LT|LE|GT|GE) expression #Compare
;
验证一下比较运算语法定义的是否正确。

发现没有错误,正确解析出树就代表语法定义的正确。接下来大家可以自己构思一下剩下的语法规则,或者添加自己的语法规则了。大概思路就是先想一个语法希望是什么样,然后编写语法规则,然后输入希望的格式验证语法规则。
编写Listener
还是老样子,编写号语法文件,我们执行Antlr4生成运行时语言为Go的命令:
java -jar 'C:\Program Files\Java\antlr\antlr-4.12.0-complete.jar' -Dlanguage=Go -no-visitor -package parser *.g4
编写监听器类
type CalcListener struct{
*parser.BaseCalcListener //继承Listener基类
*antlr.DefaultErrorListener //继承错误基类
}
//发生错误时,处理错误
func (l *CalcLister) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
}
//退出MulDiv语法时
func (l *CalcLister) ExitMulDiv(c *parser.MulDivContext) {
}
//退出AddSub语法时
func (l *CalcLister) ExitAddSub(c *parser.AddSubContext) {
}
//退出数字语法时
func (l *CalcLister) ExitNumber(c *parser.NumberContext) {
}
这里细心的小伙伴已经发现,在语法文件中使用“#”指定的节点名称在监听器中回生成一个节点方法,在Antlr4中,“#”号代表手动指定语法规则名称,需要注意的是,不要跟Token和规则名称重复。
遍历语法树

Antlr4遍历语法树时,使用DFS方式遍历树
监听模式和访问模式
Antlr4提供了两种遍历语法树的方式,监听模式和访问模式,默认是监听模式,如果希望使用访问模式的话,需要修改命令:
java -jar 'C:\Program Files\Java\antlr\antlr-4.12.0-complete.jar' -Dlanguage=Go -visitor -package parser *.g4
这样会生成访问者模式和监听者模式
calc_base_listener.go //监听者模式基类文件
calc_base_visitor.go //访问者模式基类文件
访问者模式:先遍历父节点,然后遍历子节点
监听者模式:Enter先进入父节点,Exit最后退出父节点
个人建议还是使用监听者模式,在Enter控制子节点访问,Exit做父节点子树执行逻辑。访问者模式控制能力更强,监听者模式需要遍历整个树。
至此,使用golang+Antlr4就可以定义一个属于自己的语法规则的解析器了,如果有哪里不同的可以给小编留言,我们共同学习!!!
使用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 ...
- Cocos2dx游戏开发系列笔记13:一个横版拳击游戏Demo完结篇
懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 ) 写下这些东西的同时 旁边放了两部电影 周星驰的<还魂夜> 甄子丹的<特殊身份& ...
- kotlin 写的一个简单 sql 查询解析器
package com.dx.efuwu.core import org.apache.commons.lang.StringUtils import java.sql.PreparedStateme ...
- 从零构建一个简单的 Python Web框架
为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...
随机推荐
- 在windows下使用dbus
介绍 DBUS是一种很方便的IPC远程调用的通信机制.可以很方便地调用其他进程提供的函数,甚至是不同计算机上提供的函数,内部通过TCP套接字进行相互通信. 不过甚至你可以修改成其他通信方式,比如USB ...
- 前端element ui 文件base64加密字符串 上传
<el-form-item label="附件" prop="attachment"> <el-upload :multiple=" ...
- C++中,get getline gets 用法
1.cin 2.cin.get() 3.cin.getline() 4.getline() 5.gets() 6.getchar() 1.cin>> 用法1:最基本,也是最常用的用法,输入 ...
- verilog 硬件描述语言
第一章 绪论 verilog--数字电路设计技术--ASIC/SOC芯片设计--协议pcie SATA USB--系统知识(个人计算机,芯片组,网络连接,嵌入式系统,硬件和软件的互操作) 第二章 寄存 ...
- Vuex----Getters
Getter 用于对 Store中的数据进行加工处理形成新的数据. Getter 不会修改 Store 中的原数据,它只起到一个包装器的作用,将Store中的数据加工后输出出来. const stor ...
- login_while
# -*- coding: utf-8 -*- # @Time : 2020/7/25 22:45 # @Author : Breeze # @FileName: login_while.py use ...
- Python笔记(2)——列表一:列表简介(Python编程:从入门到实践)
一.列表是什么 列表:由一系列按特定顺序排列的元素组成(列表是有序集合). 表示:用方括号[]来表示,并用逗号来分隔其中的元素. 访问:访问列表元素,可指出列表的名称,再指出元素的索引,并将其放在方括 ...
- 05.常用 API 第二部分
一.Object 类 是类层次结构的根 (父) 类. String toString () 返回该对象的字符串表示,其实该字符串内容就是对象的类型 + @ + 内存地址值. 由于 toString ...
- PyCharm2018 不使用IPython 不成功--一直显示连接控制台
每次关闭PyCharm时,都提示有后台任务在运行,一开始不知道怎么回事. 另外,发现底部的,python 控制台,启动pycharm时总有错误信息. 后来发现底部状态栏,显示1个 进程在运行. 2个地 ...
- (二).JavaScript的运算符和表达式,数据类型转化
4. 运算符和表达式 4.3 赋值运算符和表达式 1.赋值运算符 = 作用:赋值运算符就是将右边的内容赋值给左边的变量或属性. var result = 1 + 2; 2.复合赋值运算符 +=,-=, ...