基于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 放在项目目录下,以下是我调整后的目录结构:
其中:
编写语法文件
创建语法文件: 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 文件
其最终执行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)
tokenStream := antlr.NewCommonTokenStream(lexer, 0)
p := parsing.NewExprParser(tokenStream)
p.AddErrorListener(&ErrListener{})
tree := p.Prog() antlr.NewParseTreeWalker().Walk(NewListener(), tree) }
|
输入:
运行效果:
篇幅限制,源码被放在了Github上: 源代码