安装java环境,环境变量设置如下:
ANTLR 简介
- ANTLR—Another Tool for Language Recognition,Antlr 本身是使用 Java 开发的,它为包括Java,Python,C#在内的语言提供了一个通过语法描述来自动构造自定义语言的识别器(recognizer),编译器(parser)和解释器(translator)的框架。
- Antlr 使用上下文无关文法描述语言, 它允许我们定义识别字符流的词法规则和用于解释Token流的语法分析规则。然后,ANTLR将根据用户提供的语法文件自动生成相应的词法/语法分析器。用户可以利用他们将输入的文本进行编译,并转换成其他形式.
ANTLR 安装
安装有两种:一种手动使用命令行安装与执行,第二种则是借助强大的Eclipse的插件安装。 Eclipse插件安装,官网给出了很详细的教程
连接如下:
Eclipse+Antlr V4 这里就不说了。
无论那种方式,执行的过程原理都没有变。
首先说明下配置的环境:
Ubuntu 14.04 32bit
Antlr V4
jdk 1.7
- 1
- 2
- 3
安装jdk
ANTLR是用Java编写的,所以在你开始之前需要先安装Java。ANTLR v4需要Java 1.6 以上版本。安装的具体过程可以参考网上教程,挺多的。这里我安装都是jdk 1.7
版本
命令行安装
- 下载ANTLR v4,截止到2015.6,官网最新的为
antlr-4.5-complete.jar
,可以使用如下的shell命令下载:curl -O http://www.antlr.org/download/antlr-4.5-complete.jar
或者直接到[][http://www.antlr.org/download.html] 处下载,
之后,拷贝到/usr/local/lib
目录下供使用。 - 添加环境变量
修改环境变量如下:直接修改gedit ~/.bashrc
:找到CLASSPATH
行,直接再后面追加下面的变量, 完成保存之后, 执行source ~/.bashrc
生效。
:/usr/local/lib/antlr-4.5-complete.jar
- 修改快捷命令
下面的两个命令也是最常用的,这里设定别名来方便使用。
同样在直接修改gedit ~/.bashrc
找到alias
再后面加上下面两行就可。完成保存退出,执行source ~/。bashrc
即可
$ alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.5-complete.jar:$CLASSPATH" org.antlr.v4.Tool
$ alias grun='java org.antlr.v4.runtime.misc.TestRig
样例测试
命令行测试
在我们的工作目录比如说 ~/workspace
下建立一个Hello.g4
语法文件:
内容如下: 注意文件名与 与 语法一致。
// Define a grammar called Hello
grammar Hello;
r : 'hello' ID ; // match keyword ‘hello’ followed by an identifier
ID : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
这个语法很简单,只识别hello
后面接小写字母组成的字符串。
然后开始编译:
$ cd workspace
$ antlr4 Hello.g4 #这一步会自动生成几个java文件,下一步编译java
$ javac Hello*.java
可以ls
一下该目录,会发现有这么几个文件
HelloBaseListener.class HelloLexer.java HelloParser.class
HelloBaseListener.java HelloLexer.tokens HelloParser.java
Hello.g4 HelloListener.class HelloParser$RContext.class
HelloLexer.class HelloListener.java Hello.tokens
表示已经编译完成,可以使用前面重命名的命令grun
来测试,,命令格式: grun file.g4 -r [option]
其中常用的参数有这三个:
- tokens #打印出token流
- tree #用LISP表单打印出解析树
- gui #在对话框中可视化地展示解析树
测试如下:
$ grun Hello r -tree
hello world#输完执行命令之后, 在这里输入一个字符串
^D#由于程序会一直等待用户输入,所以这里使用 ctrl +D 结束输入
(r hell world)#输出结果
我们也可查看创建的tokens
:
grun Hello r -tokens
hello world
^D
[@0,0:4='hello',<1>,1:0]
[@1,6:10='world',<2>,1:6]
[@2,12:11='<EOF>',<-1>,2:0]
#-------------#
[“1,6:10 = ‘world’,<2>,1:6]表明第二个token(索引从0开始),从字符位置6到10(从0开始),有文本world,是第二个标记(ID),位置是在第一行(行数从1开始),在第6个字符处(位置是从零开始计算,tabs也算作一个独立的字符)。
最后可以测试语法规则的图示形式
grun Hello r -gui
hello world
^D
- 1
- 2
- 3
这时,会弹出一个窗口:
到现在可以修改.g4
文件来作其他的语法分析了。
推荐参考antlr 4权威指南, 毕竟antlr 4 在3的基础上变化不小,尽量参考新的资料。
编写简单计算器:
Expr.g4:
grammar Expr;
/** The start rule; begin parsing here. */
prog: stat+ ;
stat: ID '=' expr NEWLINE # assign
| NEWLINE # blank
;
expr: leftNum op=('*'|'/') rigNum # MulDiv
| leftNum op=('+'|'-') rigNum # AddSub
| '(' expr ')' # parens
;
leftNum: INT;
rigNum: INT;
ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser
WS : [ \t]+ -> skip ; // toss out whitespac
接下来运行命令:
antlr4 -no-listener -visitor Expr.g4 (antlr4 PA.g4)
javac Expr*.java
grun Expr prog -gui
当前文件夹下有这个文件,通过继承该文件编写语义规则:
语义规则文件 ExprComplier.java
import java.util.HashMap;
import java.util.Map;
import org.antlr.v4.runtime.misc.NotNull;
//import .ExprBaseVisitor;
//import .ExprParser;
public class ExprComplier extends ExprBaseVisitor<Object> {
Map<String, Double> memory = new HashMap<String, Double>();
public void getMemory() {
for (Map.Entry<String, Double> entry : memory.entrySet()) {
System.out.println("ID = " + entry.getKey() + ", Value = " + entry.getValue());
}
}
@Override
public Double visitAssign(@NotNull ExprParser.AssignContext ctx) {
String id = ctx.ID().getText();
Double value = (Double) visit(ctx.expr());
memory.put(id, value);
//System.out.println("ID is: " + id + " Value: " + value);
return value;
}
@Override
public Integer visitRigNum(@NotNull ExprParser.RigNumContext ctx) {
int rigNum = Integer.parseInt(ctx.getText());
return rigNum;
}
@Override
public Integer visitLeftNum(@NotNull ExprParser.LeftNumContext ctx) {
int leftNum = Integer.parseInt(ctx.getText());
return leftNum;
}
@Override
public Double visitParens(@NotNull ExprParser.ParensContext ctx) {
return (Double) visit(ctx.expr());
}
@Override
public Double visitAddSub(@NotNull ExprParser.AddSubContext ctx) {
int leftNum = visitLeftNum(ctx.leftNum());
String op = ctx.op.getText();
int rigNum = visitRigNum(ctx.rigNum());
if (op.equals("+")) {
return (double) (leftNum + rigNum);
}else if (op.equals("-")) {
return (double) (leftNum - rigNum);
}
return 0.0;
}
@Override
public Double visitMulDiv(@NotNull ExprParser.MulDivContext ctx) {
int leftNum = visitLeftNum(ctx.leftNum());
String op = ctx.op.getText();
int rigNum = visitRigNum(ctx.rigNum());
if (op.equals("*")) {
return (double) (leftNum * rigNum);
}else if (op.equals("/") && rigNum != 0) {
return (double) (leftNum / rigNum);
}
return 0.0;
}
编写测试文件 Test.java
//import ExprLexer;
//import ExprParser;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
public class Test{
public static void main(String[] args){
String expr="Q=2+4"+"\n"+"P=2*4"+"\n"+"R=6/2"+"\n";
ANTLRInputStream input=new ANTLRInputStream(expr);
ExprLexer lexer=new ExprLexer(input);
CommonTokenStream tokens=new CommonTokenStream(lexer);
ExprParser parser =new ExprParser(tokens);
ParseTree tree = parser.prog();
ExprComplier complier = new ExprComplier();
complier.visit(tree);
complier.getMemory();
}
}
编译该文件: javac Test.java
运行该文件: java Test