一、ANTLR入门
1、概念
-
ANTLR是一款强大的语法分析器生成工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。其主要由Java编写的。
通过ANTLR可以解析代码的grammar并且生成另一种语言的parser,例如Java,c++,python等。
2、安装
(1)Java-jdk1.7(or more high)
-
下载地址:👉
-
环境配置:
-
JAVA_HOME ,值为jdk目录
C:\Program Files\Java\jdk-17.0.1
-
CLASSPATH
.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;
-
Path
%JAVA_HOME%\bin %JAVA_HOME%\jre\bin
-
测试,进入cmd,查询java 和javac
-
(2)Antlr4
-
下载地址:👉
(这里声明,由于antlr是java写的,因此antlr即Java的一个jar包)
-
环境配置:
-
CLASSPATH,加入Antlr的jar文件的路径以及名字,格式例如:
.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar; D:\OfficeSoftware\Antlr\antlr-4.9.2-complete.jar;
-
在antlr的jar包目录下创建两个文件,antlr4.bat,内容如下:
java org.antlr.v4.Tool %*
-
grun.bat,内容如下:
java org.antlr.v4.gui.TestRig %*
-
添加antlr的jar包所在目录地址到环境变量Path中
-
测试,进入cmd,分别查询antlr命令和grun命令(有输出便成功)
-
(3)Idea或者VScode安装插件(ANTLR4)
3、使用
(1)简单入门级语法:
实例
//语法文件以grammer开头
//Algorithm与文件名相同
grammar Algorithm ;
//init文法名
//EOF 结束标志
init: expr* EOF;
expr: expr ('*'|'/') expr
| expr ('+'|'-') expr
| INT
| '(' expr ')' ;
INT: [0-9]+ ;
WS: [ \t\r\n] ->skip; //表示跳过匹配中出现的空格,制表,回车,换行符等。
grammar xx;
文法1:
文法2:
文法...
//词法规则
词法1:
词法2:
...
- 文件名和grammar关键字后跟的语法名应该一致。
- 文法规则和词法规则可以同时存在一个文件中,但文法以小写开头,词法以大写开头。
- 我们在构建一个语法文件时,一般是先考虑文法,再考虑词法。就像是我们造句,先会考虑句子的结构,再往里面填词。但是实际语法分析的过程是:输入流先经过词法分析器生成匹配的词法符号流,词法符号流经过语法分析器生成匹配的语法结构。
(2)通配符
- | 表示或
- *表示出现0次或多次
- ?表示出现0次或1次
- +表示出现1次或多次
- ~表示取反
(3)编译及运行(Vscode版)
-
编写.g4文件
-
打开终端进入到.g4文件路径下
-
编译.g4文件生成Java代码文件
antlr4 File.g4 #其中File.g4为自己创建的.g4文件
-
编译生成的Java文件
javac *.java
-
使用TestRig进行调试
#手动标准输入 grun File file -tokens #File为.g4文件名,file为该文件下的语法名 #文本输入 grun File file input.txt -tokens #查看可视化语法树 grun File file -gui
扩展:
-tokens 打印词法符号流
-tree 以 LISP 格式打印出语法分析树
-gui 在对话框中以可视化方式显示语法分析树
-ps file.ps 以 PostScipt 格式生成可视化语法分析树,然后将其存储与 file.ps
-encoding encodingname 若当前的区域设定无法正确读取输入,使用这个选项指定测试
组件输入文件的编码。
-trace 打印规则的名字以及进入和离开规则时的词法符号
-diagnostics 开启解析过程中的调试信息输出。通常仅在一些汉奸情况下才使用它产生信
息,例如输入的文本有歧义。
-SLL 使用另外一种更快但是功能稍弱的解析策略
二、基础设计语法:
1、序列:
-
//定义一个文件规则 grammar File; //语法规则定义 //file文法规则中可以有多个句子(sentence),并且用 . 作为终止符 file: (sentence '.')* ; //一个句子(sentence)中包含多个分句,用 , 作为分句的分隔符 sentence: clause (',' clause)* ; //一个分句(clause)中包含多个单词,并用 空格 作为单词的分隔符 clause: words (' ' words)*; //单词中可能是字母组成的字符串,也可以是数字 words: STRING | NUMBER ; //词法规则定义 //定义字符串规则,由小写字母(a-z)和大写字母(A-Z)组成 STRING: [a-zA-Z]+; //定义数字规则 NUMBER: [1-9][0-9]+ //可以是整数 | [0] //也可以是0 | [0-9]+[.][0-9]+ //也可以是浮点数 ; WS: [ \t\r\n] ->skip ; //跳过其中多余的空格,制表,回车,换行符。
输入文件:
I am a boy,and i like play basketball. This is an integer number of 690. There is a float number of 5.678.
编译结果:
2、选择和嵌套:
-
/* 选择和嵌套语法规则 */ grammar OptAndNest ; stem: expr* ; expr: ID '[' expr ']' //选择数组 a[1] 或者嵌套 a[b[1]] 或者 a[b[1]] | '(' expr ')' //选择带括号的表达式 (1),(a[1]),(a[1])等 | INT //选择一个整数 1 32375 ; ID: [a-z]+; //定义标识符 INT: [1-9][0-9]* //定义整形 | [0] ; WS: [ \t\r\n] ->skip ;
输入文件:
a[1] a[b[1]] (a[2]) (2) 213
编译结果:
3、优先和结合:
-
antlr的优先级通常为从上至下,选择分支排在上面的优先级高,并且一般为左向右结合
/* antlr的优先级 结合性 以及左递归 */ grammar Priority ; init: expr* EOF; // 从左向右结合 expr: expr ('*'|'/') expr | expr ('+'|'-') expr | '(' expr ')' | INT ; INT: [1-9][0-9]* | [0] ; WS: [ \t\r\n] ->skip ;
输入文件:
2 + 3 * 6 2 * 3 * 6 2 + 3 + 5
效果展示:(可以从图中看出一般都是左向右结合,即优先级从左到右)
-
因此想要从右向左则需要设置命名 <assoc=right>
//从右向左结合 expr: <assoc=right> expr ('*'|'/') expr | <assoc=right> expr ('+'|'-') expr | '(' expr ')' | INT ;
效果展示:
-
左递归:
- 首先,什么叫做左递归呢? 一个左递归的语法通常有这样的形式 : A-> Aa .而自顶向下的语法分析是无法处理左递归语法的。为什么呢?无论是递归分析还是预测分析或者是LL文法分析,在碰到左递归这种语法时都会陷入死循环当中。如果我们用递归分析,那么在分析A这个非终结符号的时候就会调用functionA,functionA将A分解成A,a,然后在我们再次碰到A的时候又会调用functionA,这样便形成了无限递归。如果我们用非递归的LL文法分析,那么在我们将把A->Aa无限次地压入到栈中,即每次弹出A都会压入Aa。所以我们必须采取手段消除左递归,下面给出标准方法。
4、核心语法标记:
三、ANTLR常见错误
1、134
-
ERROR(134): symbol char conflicts with generated code in target language or runtime
原因&解决:.g4文件中文法或者词法命名与其关键字冲突,需更换命名。
2、153
-
ERROR(153): rule row contains a closure with at least one alternative that can match an empty string
原因&解决:
3、146
-
warning(146): non-fragment lexer rule STRING can match the empty string
原因&解决:.g4文件中可能会有空字符串,即需要将部分*改为+
4、119
-
# 或者 出现 error(119): The following sets of rules are mutually left-recursive [expr]
原因&解决:.g4文件中不能识别终止,主要由于首个文法 ’:‘左右都有相同标识符,例如:
expr: expr+; // 可能会造成无限循环递归
5、169
-
error(169): rule expr is left recursive but doesn't conform to a pattern ANTLR can handle
原因&解决:左右两边都是相同的标识符,而且没有其他标识符可选择。如:
expr: expr;
本文章学习并参考antlr权威指南所撰写,部分图片来自于该文章