Antlr

什么是 Antlr?

 ANTLR™ — Another Tool for Language Recognition,其前身是 PCCTS,它为包括 Java、C++、C# 在内的语言提供了一个通过语法描述来自动构造自定义语言的识别器(Recognizer),编译器(Parser)和解释器(Translator)的框架 - - 百度百科

 ANTLR™ (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It’s widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build and walk parse trees - - Antlr Official Site

 In computer-based language recognition, ANTLR™ (pronounced Antler), or ANother Tool for Language Recognition, is a parser generator that uses LL(*) parsing - - WIKIPEDIA

为什么要有 Antlr?

简易性

 可以通过断言(Predicate)解决识别冲突
 支持动作(Action)和返回值(Return Value)
 它可以根据输入自动生成语法树,并可视化的展示出来

模块化

 复杂情况下,需要基于语法树遍历(walking the tree)生成目标代码
 Embeded action 将处理代码跟语法描述混合起来,语法复杂时使语法文件臃肿
 语法可能经常需要修改,而语法的主要表达式却不会变动,因此,Antlr 将语法识别与转换、生成(目标代码)的处理分离

Antlr 工作机制

词法分析器(Lexer)

 分析量化字符流,翻译成离散的字符组(也就是一堆 Token), 包括关键字,标识符,符号(symbols)和操作符,以供语法分析器使用

语法分析器(Parser)

 将 Tokens 组织起来,并转换成为目标语言语法(默认是 Java)定义所允许的序列

树分析器(Tree Parser)

 用于对语法分析生成的抽象语法树进行遍历,并在先序经过每个树节点的时候,进行一些定制操作

Antlr 运作流程

安装

1
2
3
4
5
6
7
8
9
10
11
12
# Download
$ cd /usr/local/lib
$ curl -O https://www.antlr.org/download/antlr-4.7.2-complete.jar

# Add antlr-4.7.2-complete.jar to your CLASSPATH
# Create aliases for the ANTLR Tool, and TestRig
$ vim ~/.bashrc
export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
alias grun='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig'

$ source ~/.bashrc

编写源文件

1
2
3
4
5
6
$ vim Benedict.g4
// Define a grammar called Benedict
grammar Benedict;
r : 'hello' Name ; // match keyword hello followed by an identifier
Name : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

词法语法分析

1
2
3
4
5
6
7
8
9
10
11
12
$ antlr4 Benedict.g4
$ ll
total 36K
-rw-r--r-- 1 benedictjin wheel 93 1 15 14:47 Benedict.g4
-rw-r--r-- 1 benedictjin wheel 321 1 15 14:47 Benedict.interp
-rw-r--r-- 1 benedictjin wheel 38 1 15 14:47 Benedict.tokens
-rw-r--r-- 1 benedictjin wheel 1.3K 1 15 14:47 BenedictBaseListener.java
-rw-r--r-- 1 benedictjin wheel 1.3K 1 15 14:47 BenedictLexer.interp
-rw-r--r-- 1 benedictjin wheel 3.7K 1 15 14:47 BenedictLexer.java
-rw-r--r-- 1 benedictjin wheel 38 1 15 14:47 BenedictLexer.tokens
-rw-r--r-- 1 benedictjin wheel 569 1 15 14:47 BenedictListener.java
-rw-r--r-- 1 benedictjin wheel 3.9K 1 15 14:47 BenedictParser.java

编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ javac Benedict*.java
$ ll
total 60K
-rw-r--r-- 1 benedictjin wheel 93 1 15 14:47 Benedict.g4
-rw-r--r-- 1 benedictjin wheel 321 1 15 14:47 Benedict.interp
-rw-r--r-- 1 benedictjin wheel 38 1 15 14:47 Benedict.tokens
-rw-r--r-- 1 benedictjin wheel 822 1 15 14:48 BenedictBaseListener.class
-rw-r--r-- 1 benedictjin wheel 1.3K 1 15 14:47 BenedictBaseListener.java
-rw-r--r-- 1 benedictjin wheel 3.8K 1 15 14:48 BenedictLexer.class
-rw-r--r-- 1 benedictjin wheel 1.3K 1 15 14:47 BenedictLexer.interp
-rw-r--r-- 1 benedictjin wheel 3.7K 1 15 14:47 BenedictLexer.java
-rw-r--r-- 1 benedictjin wheel 38 1 15 14:47 BenedictLexer.tokens
-rw-r--r-- 1 benedictjin wheel 329 1 15 14:48 BenedictListener.class
-rw-r--r-- 1 benedictjin wheel 569 1 15 14:47 BenedictListener.java
-rw-r--r-- 1 benedictjin wheel 896 1 15 14:48 'BenedictParser$SayContext.class'
-rw-r--r-- 1 benedictjin wheel 4.4K 1 15 14:48 BenedictParser.class
-rw-r--r-- 1 benedictjin wheel 3.9K 1 15 14:47 BenedictParser.java

$ grun Benedict r -tree
hello benedict
(r hello benedict)

可视化

1
2
3
$ grun Benedict r -gui
hello benedict
^D

antlr say hello example

(对 [Antlr4](https://www.antlr.org/)™ 输出界面的截图)

那些年一起踩过的坑!

给每次 antlr 的结果文件添加 package

 在 .g4 中使用 @header{ ... } 添加

1
2
3
@header{
package com.yuzhouwan.antlr;
}

确定节点与子节点之间的关系

 使用 if-elseexit/enter() 中判别,节点与子节点之间的关系,确定是否是 operator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
say
: 'say' Colon Name #Colon
;

`benedictListener` 将会得到一个定位粒度为 Colon 的访问节点

/**
* Enter a parse tree produced by the {@code Colon}
* labeled alternative in {@link benedictParser#say}.
* @param ctx the parse tree
*/
void enterColon(benedictParser.ColonContext ctx);
/**
* Exit a parse tree produced by the {@code Colon}
* labeled alternative in {@link benedictParser#say}.
* @param ctx the parse tree
*/
void exitColon(benedictParser.ColonContext ctx);

资料

Doc

Blog

Github

欢迎加入我们的技术群,一起交流学习

人工智能 1020982(高级)& 1217710(进阶)| BigData 1670647

  • 本文作者: Benedict Jin
  • 本文链接: https://yuzhouwan.com/posts/55501/
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
显示 Gitment 评论