词法和语法分析器构建

ANTLR简介

ANTLR全称ANother Tool for Languate Recognition,是基于LL(*)算法实现的语法分析器生成器和词法分析器生成器,由旧金山大学的Terence Parr博士等人于1989年开始使用java编写。截止到目前,ANTLR已经支持生成适用于Ada95、C、C#、JavaScript、Objective-C、Perl、Python、Ruby、C++和Standard ML等多种编程语言的词法和语法分析器了。

ANTLR安装

$ cd /usr/local/lib
$ wget https://www.antlr.org/download/antlr-4.7.1-complete.jar
$ export CLASSPATH=".:/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH"
$ alias antlr4='java -jar /usr/local/lib/antlr-4.7.1-complete.jar'
$ alias grun='java org.antlr.v4.gui.TestRig'

词法分析和Antlr词法定义

词法分析就是讲字符序列转换为单词序列的过程。单词是构成源代码的最小单位,它由一个或多个连续字符组成。比如对代码int year=2018进行词法分析,源代码将被转换成由5个单词组成的序列:int,\s(空格),year,=2018

antlr的词法定义比较简单,大部分的词法都可以使用简单的正则表达式表示。我们来看一个简单的例子DemoLexer.g4

lexer grammer Demo;
PLUS:'+';
MINUS:'-';
MULTIPLE:'*';
DIV:'/';
LPAREN:'(';
RPAREN:')';
NUMBER:[0-9]+;

第1行声明这是一个词法定义文件,第2-7定义4个运算符关键字和左右两个括号,第8行则是正整数的定义。

语法分析和antlr语法定义

语法分析就是根据特定的形式文法对由单词序列构成的输入进行分析并确定其语法结构的过程。比如int,\s(空格),year,=2018这5个单词序列按顺序输入到语法分析器,语法分析器就能识别出这是一条变量声明和初始化的语句。

antlr的语法定义和词法定义类似,列出所有可能的单词组合情况。我们还是从一个简单的四则运算语法定义作为例子。

DemoParser.g4

parser grammar DemoParser;
options { tokenVocab=DemoLexer; }
expr:
'(' expr ')'
| NUMBER ( MULTIPLE | DIV ) NUMBER
| NUMBER ( PLUS | MINUS ) NUMBER
;

第1行声明这是一个语法定义文件,第2行设置词法选项,说明使用哪个词法,第3-7行是运算表达式的文法定义。这里简单介绍一下,expr为我们为文法取的名称,此文法有三种情况:由括号包起来的表达式、乘除表达式和加减表达式。需要注意的是乘除表达式和加减表达式定义的顺序不能随便调换,不然会影响运算符的优先级,得到错误的语法解析树。高优先级的表达式需要先定义。

生成词法分析器和语法分析器

上面只是antlr词法和语法的定义,无法在java里直接使用,因为它不是合法的java源代码,我们需要将其转换成java代码,这个转换工作还是由antlr为我们完成。在终端运行如下命令:

antlr4 DemoLexer.g4 -package demo.antlr
antlr4 DemoParser.g4 -package demo.antlr -visitor

命令执行后,将会自动生成如下文件:

DemoLexer.java
DemoLexer.tokens
DemoParser.java
DemoParser.tokens
DemoParserBaseListener.java
DemoParserBaseVisitor.java
DemoParserListener.java
DemoParserVisitor.java

这些java文件就是我们能在程序里直接调用的词法分析器和语法分析器。

词法分析器和语法分析器的使用

生成的词法分析器和语法分析器拷贝到源码目录后就可以直接使用了,我们来看一下最常用的调用方法:

String source = "1+2+3";//我们需要解析的表达式
CharStream charStream = new ANTLRInputStream(source);
DemoLexer lexer = new DemoLexer(charStrem);//词法分析器
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
DemoParser parser = new DemoParser(tokenStream);//语法分析器
ExprContext exprContext = parser.expr();//以expr规则为起始规矩开始解析,得到语法解析树

通过以上步骤,表达式字符串最终被转换成了语法解析树,有了语法解析树,对表达式求值就变得很简单了。首先,我们先定义一个解析树访问(遍历)器MainDemoParserVisitor.java:

public MainDemoParserVisitor extends DemoParserBaseVisitor {
@override
public Integer visitExpr(ExprContext ctx) {
if (ctx.MULTIPLE()!=null) {//乘法
return Integer.parseInt(ctx.NUMBER(0)) * Integer.parseInt(ctx.NUMBER(1));
} else if (ctx.DIV()!=null) {//除法
return Integer.parseInt(ctx.NUMBER(0)) / Integer.parseInt(ctx.NUMBER(1));
} else if (ctx.PLUS()!=null) {//加法
return Integer.parseInt(ctx.NUMBER(0)) + Integer.parseInt(ctx.NUMBER(1));
} else if (ctx.MINUS()!=null) {//减法
return Integer.parseInt(ctx.NUMBER(0)) - Integer.parseInt(ctx.NUMBER(1));
} else {//对括号内表达式求值
return visitExpr(ctx.expr());
}
} }

使用访问器对解析树进行遍历并求值:

MainDemoParserVisitor visitor = new MainDemoParserVisitor();//创建访问者
Integer result = (Integer) vistor.visit(exprContext);//开始遍历语法解析树对表达式求值
System.out.println(result);

参考链接

  1. Antlr文档:https://github.com/antlr/antlr4/blob/master/doc/index.md
  2. Antlr词法语法示例:https://github.com/antlr/grammars-v4
  3. 使用Antlr构建的编程语言Kalang:https://github.com/kasonyang/kalang

用Java写编译器(1)- 词法和语法分析的更多相关文章

  1. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  2. atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结

    atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结 1. 建立AST 抽象语法树 Abstract Syntax Tree,AST) 1 ...

  3. 简易Java文本编译器(C++)

    如何使用VS写一个Java的文本"编译器 "? 所需程序: 1.Visual Studio 2.JDK 你是否因为习惯于使用VS编译C/C++程序,在学java的时候改用新编译器而 ...

  4. kafka集群搭建和使用Java写kafka生产者消费者

    1 kafka集群搭建 1.zookeeper集群  搭建在110, 111,112 2.kafka使用3个节点110, 111,112 修改配置文件config/server.properties ...

  5. Java写的斗地主游戏源码

    源码下载在最后 我们的前年的课设要求做一个斗地主程序,当时正在愁如何做界面,当时刚好在学习C#,于是就用C#完成了这个程序.一方面,当时我C#功底还很差(其实现在也不怎么样),很多地方用了“笨办法”, ...

  6. [Android] 解析android framework下利用app_process来调用java写的命令及示例

    reference to :http://bbs.9ria.com/thread-253058-1-1.html 在android SDK的framework/base/cmds目录下了,有不少目录, ...

  7. hdu 1063(java写高精度)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1063 思路:最近刚学了java,然后就迫不及待想试试java写大数的好处了,呵呵,果然是很方便啊! i ...

  8. java写的web服务器

    经常用Tomcat,不知道的以为Tomcat很牛,其实Tomcat就是用java写的,Tomcat对jsp的支持做的很好,那么今天我们用java来写一个web服务器 //首先得到一个server, S ...

  9. .NET调用Java写的WebService

    最近遇到一个用.net调用java写的webservice的应用,对方程序员提供了一个后缀为wsdl的文件,这个跟.Net里面生成的wsdl文件差不多,起初没什么概念就查了点资料,知道可以将这个wsd ...

随机推荐

  1. springboot集成mongo

    大家可以关注我的微信公众号“秦川以北” 后续更多精彩实用内容分享 ​在项目中配置,mongoDB数据库,spring整合 1. 引入pom依赖 <dependency> <group ...

  2. 加载Properties文件工具类:LoadConfig

    import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; impor ...

  3. CSMA/CD协议(载波侦听多路访问/碰撞检测) 最小帧长理解

    以下的帧长有的是指帧的时间长度,帧的时间长度=  帧长/传输时延

  4. OpenCL设计优化(基于Intel FPGA SDK for OpenCL)

    1.首先了解Intel FPGA SDK for OpenCL实现OpenCL的设计组件,包括: kernels, global memory interconnect, local memory, ...

  5. Android 进度条(ProgressBar)和拖动条(Seekbar)补充“自定义组件”(总结)

    这周结束了,我也码了一周的字,感觉还是很有种脚踏实地的感觉的,有时间就可以看看自己的总结再查漏补缺,一步一个脚印,做出自己最理想的项目. 今天我们讲两点: 1.ProgressBar: 其实前面也稍微 ...

  6. sockjs.js?9be2:1606 GET http://192.168.1.101:8080/sockjs-node/info?t=1583642185049 net::ERR_CONNECTION_TIMED_OUT错误连接方式解决方法

    在使用vue-cli脚手架创建项目的时候,在cnpm create app命令后,项目创建成功后通过npm run serve命令运行以后,控制台报错,sockjs.js?9be2:1606 GET ...

  7. PyTorch上路

    PyTorch torch.autograd模块 深度学习的算法本质上是通过反向传播求导数, PyTorch的autograd模块实现了此功能, 在Tensor上的所有操作, autograd都会为它 ...

  8. FTP服务器搭建及自动备份设置

    本次随笔内容主要是FTP服务器搭建. 其实去年十月服务器就搭建完了.当时写了个PPT保存了一下,准备以后写博客,结果时隔快一年我自己都快要看不懂我自己写的PPT了  ( = o = ) 不过还是尽量尝 ...

  9. vue element Admin - 修改浏览器标签名 + 添加tagView标签 +固定导航头部 + 添加侧边栏Logo

    1 .修改浏览器标签名称: 修改浏览器标签名称在文件:\src\settings.js   image.png 2 .修改固定头部Header和侧边栏 Logo:   image.png 1)侧边栏文 ...

  10. vue跳转页面问题记录

    跳转到别的页面带参数 const space = this.pageHelperspace['search'] = this.searchconst query_params = Object.ass ...