antlr介绍
注意: 文字来自《antlr4权威指南》
ANTLR 语言识别的一个工具 (ANother Tool for Language Recognition ) 是一种语言工具,它提供了一个框架,可以通过包含 Java, C++, 或 C# 动作(action)的语法描述来构造语言识别器,编译器和解释器。 计算机语言的解析已经变成了一种非常普遍的工作,在这方面的理论和工具经过近 40 年的发展已经相当成熟,使用 Antlr 等识别工具来识别,解析,构造编译器比手工编程更加容易,同时开发的程序也更易于维护。
语言识别的工具有很多种,比如大名鼎鼎的 Lex 和 YACC,Linux 中有他们的开源版本,分别是 Flex 和 Bison。在 Java 社区里,除了 Antlr 外,语言识别工具还有 JavaCC 和 SableCC 等。
和大多数语言识别工具一样,Antlr 使用上下文无关文法描述语言。最新的 Antlr 是一个基于 LL(*) 的语言识别器。在 Antlr 中通过解析用户自定义的上下文无关文法,自动生成词法分析器 (Lexer)、语法分析器 (Parser) 和树分析器 (Tree Parser)。
antlr 的应用
编程语言处理
识别和处理编程语言是 Antlr 的首要任务,编程语言的处理是一项繁重复杂的任务,为了简化处理,一般的编译技术都将语言处理工作分为前端和后端两个部分。其中前端包括词法分析、语法分析、语义分析、中间代码生成等若干步骤,后端包括目标代码生成和代码优化等步骤。
Antlr 致力于解决编译前端的所有工作。使用 Anltr 的语法可以定义目标语言的词法记号和语法规则,Antlr 自动生成目标语言的词法分析器和语法分析器;此外,如果在语法规则中指定抽象语法树的规则,在生成语法分析器的同时,Antlr 还能够生成抽象语法树;最终使用树分析器遍历抽象语法树,完成语义分析和中间代码生成。整个工作在 Anltr 强大的支持下,将变得非常轻松和愉快。
文本处理
当需要文本处理时,首先想到的是正则表达式,使用 Anltr 的词法分析器生成器,可以很容易的完成正则表达式能够完成的所有工作;除此之外使用 Anltr 还可以完成一些正则表达式难以完成的工作,比如识别左括号和右括号的成对匹配等。
基本环境配置
命令方式生成文件
下载antlr
https://www.antlr.org/download/antlr-4.7.2-complete.jar
启动antlr
1 | alias antlr4='java -Xmx500M -cp "/home/deeplearning/antlr/antlr-4.7.2-complete.jar:$CLASSPATH" org.antlr.v4.Tool' |
测试案例
定义antlr Hello.g4文件
注意: 逗号必须对齐1
2
3
4grammar Hello; //定义一个名为Hello的语法
r : 'hello' ID ; // 匹配一个关键字hello和一个紧随其后的标识符
ID : [a-z]+ ; // 匹配小写字母组成的标识符
WS : [ \t\r\n]+ -> skip; // 忽略空格 Tab 换行
运行生成对应的文件和代码
antlr4 Hello.g4
javac Hello*.java
运行生成树图
grun Hello r -gui
IDEA中antlr4环境配置
参考文章
https://www.baeldung.com/java-antlr
在maven 项目src/main下创建一个目录
mkdir -p antlr4/cn/xh/parse
编写MyParse.g4文件
1 | grammar MyParse; |
在pom.xml文件中添加如下内容
1 | <antlr4.version>4.7.2</antlr4.version> |
输入maven命令打包
1 | mvn package |
结果
在target/generated-sources/antlr4/cn/xh/parse 中产生对应的文件
antlr基本语法详解
参考《antlr4权威指南》
定义文件头
使用Antlr 的语法规则来定义算术表达式文法,文件头部是 grammar 关键字,定义文法的名字,必须与文法文件文件的名字相同。1
grammar MyParse;
自定义一个算术表达式
表达式规则:
算法的优先级需要通过文法规则的嵌套定义来体现,加减法的优先级低于乘除法,表达式 expr 的定义由乘除法表达式 multExpr 和加减法算符 (‘+’|’-‘) 构成;同理,括号的优先级高于乘除法,乘除法表达式 multExpr 通过原子操作数 atom 和乘除法算符 (‘*’|’/’) 构成。
程序有一个语句构成,语句有表达式或者换行符构成。1
2
3
4
5prog: stat
;
stat: expr
|NEWLINE
;
在 Antlr 中语法定义和词法定义通过规则的第一个字符来区别, 规定语法定义符号的第一个字母小写,而词法定义符号的第一个字母大写。算术表达式中用到了 4 类记号 ( 在 Antlr 中被称为 Token),分别是标识符 ID,表示一个变量;常量 INT,表示一个常数;换行符 NEWLINE 和空格 WS,空格字符在语言处理时将被跳过,skip() 是词法分析器类的一个方法1
2
3
4ID:('a'..'z'|'A'..'Z')+;
INT:'0'..'9'+;
NEWLINE:'\r'?'\n';
WS:(' '|'\t'|'\n'|'\r')+{skip();};
案例
antlr文件编写
MyParse.g4文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22grammar MyParse;
//parser
prog:stat
;
stat:expr|NEWLINE
;
expr:multExpr(('+'|'-')multExpr)*
;
multExpr:atom(('*'|'/')atom)*
;
atom:'('expr')'
|INT
|ID
;
//lexer
ID:('a'..'z'|'A'..'Z')+;
INT:'0'..'9'+;
NEWLINE:'\r'?'\n';
WS:(' '|'\t'|'\n'|'\r')+{skip();};
使用java调用分析器
参考spark源码中ParseDriver.scala文件中AbstractSqlParser类parse方法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
29
30
31
32
33
34
35
36
37
38
39
40
41
42package com.xh.antlr4;
import cn.xh.parse.MyParseLexer;
import cn.xh.parse.MyParseParser;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.spark.sql.catalyst.parser.UpperCaseCharStream;
public class UserMyParse {
public static void run(String expr) throws Exception {
//对每一个输入的字符串,构造一个 CodePointCharStream 流 in
UpperCaseCharStream in = new UpperCaseCharStream(CharStreams.fromString(expr));
//用 in 构造词法分析器 lexer,词法分析的作用是产生记号
MyParseLexer lexer = new MyParseLexer(in);
//用词法分析器 lexer 构造一个记号流 tokens
CommonTokenStream tokens = new CommonTokenStream(lexer);
//再使用 tokens 构造语法分析器 parser,至此已经完成词法分析和语法分析的准备工作
MyParseParser parser = new MyParseParser(tokens);
//最终调用语法分析器的规则 prog,完成对表达式的验证
parser.prog();
}
public static void main(String[] args) throws Exception {
String[] testStr = {
"2",
"a+b+3",
"(a-b)+3",
"a+(b*3)"
};
for (String s : testStr) {
System.out.println("Input expr:" + s);
run(s);
}
}
}