词法和语法分析器构建

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. Dynamics 365 CRM On premise Unable to Load plug-in assembly

    背景介绍: 本地部署Microsoft Dynamics CRM 9.0正常可用,后打补丁到9.0.16.7,打开系统quote报 “ Unable to Load plug-in assembly” ...

  2. windows:shellcode 远程线程hook/注入(二)

    https://www.cnblogs.com/theseventhson/p/13218651.html   上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...

  3. cobbler多机定制安装

    目录 cobbler多机定制安装 1. cobbler服务端部署 2. 客户端安装 3. 定制安装配置 4. 安装 client1开机 client2开机 cobbler多机定制安装 1. cobbl ...

  4. Qt自定义控件之仪表盘3--雷达扫描图

    1.设计思想 雷达扫描图,在影视作品中见到较多,比如飞机雷达.舰艇雷达,有一个扫描线转圈代表雷达一周旋转或一个批次的收发,发现目标就在表盘上标记位置.和汽车仪表盘类似,汽车仪表盘有底盘背景图.同圆.刻 ...

  5. 44-final, finally, finalize的区别

    final—修饰符(关键字) 如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承. 因此一个类不能既被声明为 abstract的,又被声明为final的.将变量或方法声明为 ...

  6. Android SQLite轻量级数据库(简单介绍)

    SQLite它是相当于嵌入到安卓里的一个小数据库吧, 它也可以使用SQL语句进行数据库的增删改查操作,但是是SQL1992的语句. 然后SQLite也有自己的语句,但是学过SQL的应该都会发现,它比较 ...

  7. python7.4邮件发送

  8. 008_用go语言实现简单的冒泡排序

    冒泡排序是各个语言中的基本排序算法,本次我们用go语言实现简单的冒泡排序 package main import "fmt" // [13,10,5,7,2] // [10,13, ...

  9. docker 启动redis 报错!

    首先通过命令进入: docker  exec -it ‘容器名’  redis-cli 错误信息: There was an unexpected error (type=Internal Serve ...

  10. VMware Workstation pro无法在Windows上运行,检查可在Windows上运行的此应用的更新版本

    我的Windows版本是win10-1903,VMware版本比较老旧是VMware-10:国庆节后微软推送了一个新的更新补丁,更新之后发现VMware无法打开(未更新前正常). 更新补丁详情如下: ...