CMM语言解释器构造实践(二)——JavaCC等编译工具的使用

前言

上篇博文中我们已经简要介绍了CMM语言的基础特点,以及整个编译器项目的基本开发流程。下面就对开发的第一个阶段“编译工具的使用”详细说明。


编译工具的发展

  • lex/yacc (flex/bison)
    generate bottom-up parsers which are often hard to understand, hard to debug and give weird error messages
  • antlr4 (Java) and antlr3
    produces a recursive descent top-down parser which is much easier to understand, you can actually debug it easily, you can only use subrules for a parse operation (e.g. just parse expressions instead of the full language)
  • SableCC (Java)
  • and SimpleParse (Python)
  • JavaCC
    ……

在编译原理的课堂上,我们粗略接触了lex/yacc等最初的编译工具。这类编译器最大的特点,是自底向上分析,通常很难理解,也难以在开发过程中进行调试与错误处理。

Antlr (ANother Tool for Language Recognition)、JavaCC等编译工具,是如今更为常用的编译器生成工具,此类编译器由于具有自顶向下分析的特点,更易被人接受,也更易使用,极大的提高了开发效率。这样的编译工具所生成的解析器叫做递归下降解析器(recursive-descent parser),属于自顶向下解析器(top-down parser)的一种。

除此之外,还存在着SableCC,以及针对解释型语言Python的SimpleParse等编译工具。

在本项目中,笔者主要采用了JavaCC编译工具,下面来对其进行更进一步的了解。


JavaCC简介

JavaCC是一个词法分析生成器和语法分析生成器。

词法分析和语法分析是处理输入字符序列的软件构件,编译器和解释器协同词法分析和语法分析来“解密”程序文件,不仅如此,词法分析和语法分析有更广泛的用途。

JavaCC并不是一个词法分析器或者语法分析器,它只是一个生成器。就是说,它读取文本后,基于一定的规则产生词法分析器和语法分析器的Java代码。

词法分析和语法分析变的越来越复杂,软件工程师直接用Java进行词法分析和语法分析时必须要充分考虑各规则间的相互作用。

例如,在对 C语言的词法分析中,处理整型常量和浮点常量的代码是不能分开的,因为浮点数和整数的前面部分是一样的。使用诸如JavaCC语法分析产生器,对整型常量 和浮点常量是可以区分的,它们的共同点可在代码生成过程中提取出来。这种模块性意味着JavaCC文件比直接的Java程序更容易写,更容易读,也更容易 修改。通过JavaCC语法分析生成器的使用,软件工程师可以节省大量的时间,并且软件的质量也更高。

总之,如何使用JavaCC呢?一句话来讲:你只需要叙述“待编译语言”的语法规则,而JavaCC自动替你生成词法、语法分析器。
也就是说,JavaCC并不是编译器,而是生成编译器的工具,而且它所生成的编译器只包括了词法分析、语法分析过程,后续的语义分析等过程,还是需要你来完成。
而为什么这个工具叫“Java”CC呢?这是因为JavaCC生成这个编译器,是由Java语言编写的。

那么,我们的工作就演化成了三步:首先,配置JavaCC环境。其次,叙述语法规则,也就是编写.jj文件。最后,使JavaCC处理.jj文件,以生成若干个类文件(也就是我们的编译器)。

上述过程工作完成后,JavaCC为我们生成了相应的编译器。那么这个编译器又是如何工作的呢?
我们先不考虑代码怎么写,只思考开发过程。假如我们现在已经得到了这个编译器,并假设我们需要从命令行中输入源码,我们输入一个简单的赋值语句:sp = 100;,那么编译器会做什么事情呢?

毫无疑问,编译器会对我们所输入的源码进行解析,该解析过程包括两个阶段:

recog

在第一阶段,使用词法分析器将输入解析成一个个单词(token),即词法分析(lexical analysis),对应的分析程序叫做lexer,负责将符号(token)分组成符号类(token class or token type)
在第二阶段,对第一阶段的tokens进行分析,解析出句子的结构,对应的分析程序为parser。默认JavaCC会构建出一棵分析树(parse tree)或叫语法树(syntax tree)。

这里简要的介绍一下语法树 (Syntax Tree)。

语法树的叶节点token,而父结点是包含其子结点的词组名(phase),线性的句子也就是对语法树的序列化。
生成语法树的好处有:
1) 树形结构易于遍历和处理,易于理解,方便应用代码做进一步处理。
2) 多种解释或翻译的应用代码都可以重用一个解析器。JavaCC还支持将应用处理代码嵌入到语法中。
3) 对于因为计算依赖而需要多趟处理的翻译器来说,语法树使得可以被高效地多次遍历,而无需每次解析。

到这里,JavaCC的基本工作原理就介绍完成了。


JavaCC环境配置

关于JavaCC的环境配置,已经有各式各样的使用教程了,这里就不再赘述。总的来说,可采用如下两种方式:

  1. 通过命令行:先在官网下载对应的配置包,再配置环境变量,最后通过javacc *.jj命令,生成若干个类文件。
  2. 通过IDE插件:在eclipse或idea等IDE的插件库中,都含有JavaCC插件,只需搜索后安装即可。

编写语法规则

配置好环境之后,我们就进入了第二步,也是核心的一步:叙述语法规则,即编写.jj文件。

.jj文件不属于某种高级语言,其编写有自己的语法规则,但非常简单,极易上手。文件中主要包含以下四个部分:参数配置、程序入口、词法叙述、语法叙述,我们下面就把一个.jj文件拆解开来看,分别进行这四个部分的编写。

在正式开始之前,我们先通过官方文档上的一个简单的例子,来整体了解一下.jj文件是如何叙述语法的。

/*
* javacc-5.0/examples/SimpleExamples/Simple1.jj
*/

options {
  LOOKAHEAD = 1;
  CHOICE_AMBIGUITY_CHECK = 2;
  OTHER_AMBIGUITY_CHECK = 1;
  STATIC = true;
  DEBUG_PARSER = false;
  DEBUG_LOOKAHEAD = false;
  DEBUG_TOKEN_MANAGER = false;
  ERROR_REPORTING = true;
  JAVA_UNICODE_ESCAPE = false;
  UNICODE_INPUT = false;
  IGNORE_CASE = false;
  USER_TOKEN_MANAGER = false;
  USER_CHAR_STREAM = false;
  BUILD_PARSER = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值