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构建一个自己的语言解析器(二)的更多相关文章

  1. 自己动手写一个编译器Tiny语言解析器实现

    然后,上一篇文章简介Tiny词法分析,实现语言.本文将介绍Tiny的语法分析器的实现. 1 Tiny语言的语法 下图是Tiny在BNF中的文法. 文法的定义能够看出.INNY语言有以下特点: 1 程序 ...

  2. atitit.java解析sql语言解析器解释器的实现

    atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...

  3. 自己动手实现一个简单的JSON解析器

    1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...

  4. 一个简单的json解析器

    实现一个简单地json解析器. 两部分组成,词法分析.语法分析 词法分析 package com.mahuan.json; import java.util.LinkedList; import ja ...

  5. 用c#自己实现一个简单的JSON解析器

    一.JSON格式介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着很多优点.例如易读性更好,占用空间更 ...

  6. VS Extension+NVelocity系列(一)——构建一个简单的NVelocity解析环境

    一.前言 本节我们将实际实现一个简单的NVelocity解析环境,以便为以后的实例做一些基本工作,虽然NVelocity如何使用已经属于老掉牙的话题,但我只能专门挑出来一章来做铺垫.人生就是这样无奈啊 ...

  7. 用c#写一个json的万能解析器

    CommonJsonModel .cs /// <summary> /// 万能JSON解析器 /// </summary> public class CommonJsonMo ...

  8. WCF学习——构建一个简单的WCF应用(二)

    我们接着上一篇文章进行讲解 http://www.cnblogs.com/songjianhui/p/7060698.html 一:客户端通过添加引用调用服务 WCF应用服务被成功寄宿后,WCF服务应 ...

  9. kotlin 写的一个简单 sql 查询解析器

    package com.dx.efuwu.core import org.apache.commons.lang.StringUtils import java.sql.PreparedStateme ...

  10. 从零构建一个简单的 Python Web框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

随机推荐

  1. 2022-07-10 第一小组 张明旭 前端CSS学习记录

    今天是正式学习的第二天,昨天那个作业项目真的难,自己做一会就得问这问那,还有大神带一带(?_?)... 今天学习的内容是前端中的CSS,男上加男..... 知识点: Css层叠式表(网页美化) 1.定 ...

  2. 自动化测试工具selenium的常用定位方法

    定位方法不仅限于这些,我也会随时补充,大家有其他补充或建议可以在评论区一起讨论哦!!!     [打开链接]drive.get("https://www.baidu.com")   ...

  3. Loadrunner——调试及脚本编译

    调试一般用于运行代码是出现的错误. loadrunner调试方式:断点.单步跟踪.日志输出.值查看器等, 断点设置 断点插入的位置:非空行或非语句的起始,简单来说呢就是断点打在函数前(取消断点就直接在 ...

  4. 如何申请ios证书

    第一次申请ios证书  记录下来 第一步 随便找个可以在线生成ios证书的网站 在这里生成csr文件 https://www.yunedit.com/update/ioszhengshu/list 第 ...

  5. mysql零基础-1

    数据库概述 为什么要使用数据库 持久化 DB:数据库 DBMS:数据库管理系统 SQL:结构化查询语言 数据库与数据库管理系统关系 数据库管理系统(DBMS)可以管理多个数据库,一般开发人员会针对每一 ...

  6. 当jar包执行时,内嵌的文件找不到时,可以这样解决!

    1.加载是可以加载到的,但是只能是以流的形式存在. 2.如果要按文件进行加载,可以新建一个文件,然后以流的形式写入到新的文件中. 3.加载这个新的文件来进行处理.

  7. AutoCAD2018

    「AutoCAD_2018_SC.exe」https://www.aliyundrive.com/s/GvpR9yg6TWg 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线 ...

  8. Js-document操作

    # 直接获取标签 document.getElementById('gundong') #获取id为gundong的元素 document.getElementsByClassName('qalist ...

  9. 2月22日javaweb学习之Maven

    Maveb是专门用于管理和构建java项目的工具,它的主要功能有: 1.提供一套标准化的项目结构. 2.提供一套标准化的构建流程(编译.测试.打包.发布......) 3.提供了一套依赖管理机制 Ma ...

  10. Linux /proc 目录

    /proc 目录 /proc 文件目录是一个伪文件,它只存在于系统内存中,而不占用外存空间.它以文件系统的方式为用户提供访问内核数据的操作接口.目录下主要包含进程和状态的信息. /proc 下文件含义 ...