Antlr-Golang

基于Antlr我们可以快速创建一门DSL语言,其词法分析器和语法解析器,可以帮助节省很多时间,使我们更加专注与语法模型以及语义的实现。

Antlr是用Java语言开发的,为了运行Antlr,需要安装JDK,目前V4版本需要1.6以上版本,本文中使用的版本是 openjdk 11.0.21

Antlr支持生成非Java语言的目标代码,因为平时用Golang比较多,所以本文的案例也是用Golang实现。

安装

按照 官方文档 ,我们需要先下载

1
curl -O https://www.antlr.org/download/antlr-4.13.1-complete.jar

然后将 antlr-4.13.1-complete.jar 放在项目目录下,以下是我调整后的目录结构:

其中:

  • parser放置了antlr工具包、 Expr.g4(语法文件)、generate文件、generate.sh文件

  • parsing中是antlr4生成的代码

  • samples中放了一些案例

  • expr_test.go是测试文件

编写语法文件

创建语法文件: Expr.g4, 文件名需要和语法名相同,这个语法用来实现一个计算器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
grammar Expr;

prog: stat+ ;

stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;

expr: expr ('*'| '/') expr
| expr ('+'| '-') expr
| INT
| ID
| '(' expr ')'
;

ID : [a-zA-Z]+ ; //匹配标识符
INT: [0-9]+ ; //匹配整数
NEWLINE: ';' ; //匹配终止符
WS : [ \r\t\n]+ -> skip ; //丢弃空白符

然后就是运行 generate 文件

1
go generate ./...

其最终执行generate.sh 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

while getopts ":n:p:" opt; do
case $opt in
n) name="$OPTARG"
;;
p) package="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
exit 1
;;
esac

case $OPTARG in
-*) echo "Option $opt needs a valid argument"
exit 1
;;
esac
done

alias antlr4='java -Xmx500M -cp "./antlr-4.13.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
antlr4 -Dlanguage=Go -no-visitor -package "$package" -o ../"$package" "$name".g4

运行完后,会在parsing目录中生成一堆文件,里面包含了golang代码文件。

这里与官网稍微有点不同,我加了两个参数,-n 指定语法名 -p 指定生成的包以及文件名,毕竟如果语法比较多的情况下,都放在一个目录里,会比较混乱。

编写语义

以下是核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

func TestExpr(t *testing.T) {

//接收一个文件流
inputStream, err := antlr.NewFileStream("./samples/expr_1")
if err != nil {
log.Fatal(err)
}

//从文件流中创建一个词法解析器
lexer := parsing.NewExprLexer(inputStream)

//从词法解析器中创建一个Token Stream
tokenStream := antlr.NewCommonTokenStream(lexer, 0)

//从Token Stream中创建一个语法解析
p := parsing.NewExprParser(tokenStream)

//添加错误监听器
p.AddErrorListener(&ErrListener{})

//开始解析并生成语法树
tree := p.Prog()

//遍历树,并设置监听器
antlr.NewParseTreeWalker().Walk(NewListener(), tree)
}

输入:

运行效果:

篇幅限制,源码被放在了Github上: 源代码


Antlr-Golang
https://www.xinyublog.com/antlr/anltr-calculator/
作者
蚂蚁
发布于
2023年12月8日
许可协议