嵌入式Java编译器的研究与设计*

2018-04-11 09:13:50,,
单片机与嵌入式系统应用 2018年4期
关键词:代码生成分析器词法

,,

(黑河学院 计算机与信息工程学院,黑河 164300)

引 言

随着计算机技术、网络通信技术及微电子技术的迅猛发展和广泛应用,嵌入式技术的应用已经深入到当今世界的各个角落。无论是工业现场的数据采集与测控系统,还是数字化家用电器都从单机、局部的检测控制,发展成为以高性能的嵌入式系统为核心、依靠有线/无线网络、能够进行远程监测和控制的复杂系统。许多高级的面向对象和面向网络的程序设计方法大行其道,如C#和Java等渐渐成为程序设计的主流,甚至于在嵌入式系统中也出现了J2ME等先进的程序设计技术。目前广泛使用的网络编程语言Java是一种适合于分布式计算的新型面向对象的,适应网络发展需要的程序设计语言。但JVM(Java虚拟机)对硬件环境的要求比较高,需要较大的内存和足够高的运行速度,这样的硬件要求使设备的成本过于昂贵,从而限制了Java语言在低档嵌入式系统中的应用。

所以到目前为止,未能检索到应用于MCS-51系列之类较低档的8位单片机的Java编译器。而作为目前国内使用最多,应用最广的嵌入式内核,为了实现与Internet的连接,依然沿用传统的设计方法,十分费时费力,所以只有极少数实力雄厚的公司和技术精湛的个人才能对其进行开发[1]。因此将Java语言引入MCS-51等廉价8位单片机的应用设计,可让国内嵌入式系统技术和家庭信息化技术跟上国外技术发展的潮流,使广大的中小型公司和初入门的嵌入式系统设计者都能很方便地实现与网络的连接,是一件非常重要和非常紧迫的工作。

本文的目标是针对国内外嵌入式系统的应用现状,在PC机的Windows环境下使用VC 6.0开发工具,提出用编译的方法,设计一款能直接生成MCS-51系列单片机的目标代码,不依赖操作系统和JVM的支持多线程的嵌入式Java编译器。使设计者能用Java语言编程建立设备的Web页面,使用普通的浏览器就可访问各种电器设备,实现远程的实时监测和控制功能。由于Java语言是面向对象和面向网络的,比汇编和C语言都更接近自然语言,可以降低嵌入式设计技术的难度,而MCS-51系列单片机具有最大的应用范围,可以实现成本低廉的工业环境甚至家庭电器的监测和控制。这种基于互联网的嵌入式监控系统技术的普及,能使信息技术在全社会的各个方面得到更广泛的应用。

1 Java技术与嵌入式系统的矛盾剖析

Java程序设计语言是一种面向对象的,构造精美的通用程序设计语言,其正在迅速地发挥它的潜能,改变着软件开发方式,主要由以下模块组成:Java源语言、Java的class文件格式、Java应用程序接口和JVM。其中,JVM和Java class文件是任何传统Java运行环境必不可少的。Java源语言通过编译器(命令工具Javac)编译为字节码(Class文件),它为Java源程序提供了隔离运行环境的二进制形式的服务,这正是Java虚拟机需要的;软件程序调用Java应用程序接口访问系统资源,这就注定Java应用程序接口的class库文件与主机平台是密切相关的。在一个平台支持Java程序之前,必须在这个特定平台上明确地实现API(应用程序接口)的功能,API可以轻松地通过本地方法实现本地资源的访问。最后,通过Java虚拟机解释运行Java字节码。Java编译系统的体系结构如图1所示。

图1 Java体系结构

与传统开发语言相比,Java比C更具条理性,比C++更容易学,弥补了C++中的一些弊端。但是标准Java语言是一门解释型语言,运行在Java虚拟机上的Java应用程序执行速度最快,也只能满足ms级的软实时需求。

虽然Sun公司不断地改进优化运行时的环境(HotSpot Java虚拟机),但是运行速度还是不能满足实时嵌入式系统的时间要求。Java语言的垃圾回收机制随时都会让系统停止运行,这将导致时间的不可预测性,会造成严重的后果,严重违背了嵌入式系统的宗旨[1]。

对于嵌入式编程而言,选择Java语言作为开发工具看起来有些难以想象。传统的Java技术在嵌入式系统应用中有很多的不足,如动态类加载,以及垃圾收集随时停顿系统的运行等,使得 Java技术的时间特性具有不确定性和抖动;同时,Java语言是一门解释型的语言,其性能会比C/C++编写的同样程序运行时慢许多。由于这些因素,致使Java语言不能在嵌入式系统中得到广泛的应用[2]。

2 编译器的设计

编译器是嵌入式技术的重要组成部分,它是运行在主机平台上,为另一个不同的目标平台生成可执行代码的特殊的编译器[3]。本文编译器的任务是将Java源程序编译成MCS-51系列单片机的可执行文件,其设计采用单遍扫描的编译程序结构。

现代编译器开发技术已经存在许多非常标准的形式化方法,这些方法将极大地改变编译器开发的难度。本文运用Windows下VC 6.0开发工具,参照实时版jRate及Sun公司的GJC编译系统[4],使用C++作为宿主语言,完成实时Java交叉编译器的词法分析器、语法分析器和语义分析器设计,最后实现Java语言源程序的编译并生成MCS-51系列单片机的目标代码。

2.1 词法分析器设计

词法分析程序完成的是编译第一阶段的工作。词法分析器的任务是对输入的符号串形式的源程序进行最初的加工处理,其扫描读入高级语言源程序中的每个字符,识别出原程序中有独立意义的源语言单词,用某种特定的数据结构对它的属性予以表示和标注,即Token码(Token集合由词法文法或正规文法表示)。

这些由单词符号组成的Token码传递给语法分析器作进一步分析[5]。目前绝大多数的编译器是将词法分析程序设计成语法分析程序的一个子程序或者协作程序进行实现,当语法分析部分调用词法分析器时,执行 “取下一个Token码”指令,词法分析器读取输入字符,直到识别出下一个字符,如图2所示。

图2 语法分析器与词法分析器的调用关系

在实时Java交叉编译器中,词法分析器是通过Scanner类实现的。在头文件scanner.h中声明了整型Token,识别出当前字符是什么、关键字、标识符或者是Java语言的符号等等。假设词法分析器识别出当前处理的单词类型是标识符,则必须要提供该标识符的具体名字,方便将其存储到符号表中;同时,程序中还声明了当前字符的起始位置(Pos)、结束位置(endPos)和临时记录位置(temPos),这些都是位置记录类Position的实例。

Position类用于存储扫描器扫描到的字符位置,其line和col属性分别表示字符所在行与列,这些内容在源程序出现词法错误时,用来提供错误位置的相关信息,并且提供Set方法用于为Position实例赋值,Reset方法将行、列置零。

实时Java词法分析器的初始化在Scanner的构造函数中完成的,其主要完成将输入文件中的字符读到内存的缓冲区内,预置当前字符的位置,并且调用NextToken()方法读取下一个Token码,NextToken()方法也是语法分析器调用词法分析器的接口。由于整个输入文件在词法分析器初始化时已经读入内存缓冲区中,读取字符就变得方便许多,只需移动字符指针即可,并将该字符赋值给词法分析器中当前正在处理字符的整型变量ch。

具体实现方式见scanchar()方法,同时也应维护当前字符的位置相关信息。词法分析器还有一个重要任务——注释和空格的过滤,本文通过Scanner类的ScanCommentChar()和SkipComment()两个方法实现,它们分别表示扫描注释标志和跳过注释内容。

根据上面所述,当读取到一个字符时,可根据不同的情况(case语句)调用相应的处理函数进行处理。本文设计的Scanner类的主要属性如下所示:

class Scanner{

Position pos;

//当前的token的起始位置

Position endPos;//当前的token的结束位置

Position tmpPos;//临时记录用位

void ScanCommentChar();//扫描注释标志

void SkipComment();//跳过注释内容

......

void NextToken();//扫描下一个token

void OutputToken();//输出当前的token

};

2.2 语法/语义分析器设计

根据Java语言语法规范,为了使方法具有确定性,本文选择按照LL(1)文法采用递归下降法进行总体语法分析,并结合使用自底向上分析法的算符、优先规则处理文法符号求出其属性值[6]。本文编译器的语法/语义分析器是采用语法制导的翻译模式,通过递归下降分析法和算符分析优先法实现的。编译器的词法分析器、语法分析器、语义分析器及抽象语法树之间的关系如图3所示。

图3 嵌入式Java编译器的前端流程图

实时Java编译器的语法/语义分析器主要是由Syntax类完成的,该语法分析器的主要任务是将输入的Token字符串通过递归下降分析法构造成抽象语法树,同时进行语义分析,遍历语法树并在语法树的各结点处按语义规则进行计算。

Syntax类的MakeTree方法实现了各种语法树的构造。Word结构用于记录每个单词的信息包括其详细属性,所在源程序的行和列,以及一个存储值的联合类型。在本文中,语法和语义的检查方法是基于语义数据结构的,作者在数据结构中定义了一个符号表——HashTable,它将源程序的单词名称作为关键字。每个名字包括两个属性:该字符起始位置的偏移量和名字的长度。

在语法分析过程中,有时候会遇到很多语法及语义错误。但是当遇到错误时,分析过程不会停止,它会继续完成整个程序的分析。错误处理是由Syntax类中的Skip()方法实现的,并且通过TellError()报告错误的相关信息,并写入记录文件。错误记录包括语法错误的类型,所在源程序的行与列等。本文设计的Syntax类的部分属性如下所示:

class Syntax{

viod Skip();

void TellError(const char * reason);//报告错误

……

int Id_To_Index(const char * id);//由标识符查找索引,

//不存在返回-1

void MakeTree(); //构造语法树

};

2.3 生成目标代码

目标代码生成作为实时Java编译器的最后阶段,把经过语法分析或优化后的中间代码作为输入,将其转换成特定机器的机器语言或汇编语言作为输出,这样的转换程序称为代码生成器。对于嵌入式交叉编译器而言,代码生成是非常复杂的过程,因为其不但依赖高级语言的特征,而且还依赖于硬件平台,与运行在硬件平台上的操作系统也有密切的关系[7]。

总体来讲,代码生成器的输入包括前端产生的源程序的中间表示与符号表信息,负责存储的管理,与编译器的前端协作将符号表中的数据对象映射为运行时存储器中数据对象的地址。目标代码的输出可以是多种多样的:可重定位的机器语言、绝对机器语言和汇编代码。

前面两种语言的设计难度比较高,而且几乎每个系统都有其对应的汇编程序,因此本文选用生成汇编代码作为代码生成的目标文件程序,这样就可以避免编写编译程序的目标机器代码部分,同时可以利用汇编器的宏功能帮助代码生成。

本文的代码生成过程是由语法分析器Syntax类的WriteAsm()函数完成的,通过该函数完成抽象语法树的遍历。程序运行时,该函数会为进入的函数或者过程在内存中开辟一个栈区并且按栈的特性进行存储分配,其需要的存储空间就被分配于栈顶,函数返回时释放所占有的空间。在成功存储“标识符”等符号后,同时也将生成的汇编程序的数据段写入符号表,符号表用以记录单词符号与其索引号的对应关系,符号的索引号用于表明相应符号在汇编语言文件数据段中的地址标号。当符号再次出现时,通过索引号在汇编语言的代码段中引用。代码生成程序的主要内容如下所示,主要给出了while循环语句及各种运算符的函数定义:

void Asm_WhileBegin();//循环开始

void Asm_WhileLoop(const char * jump);

//循环判断(jump=跳出循环的跳转语句)

void Asm_WhileEnd();//循环结束

……

void Asm_Expression(struct Word * theoptr_ptr); //处理表达式

void Asm_Comment(const char * comment); //加入注释

void WriteAsm(); //生成汇编语言

2.4 实时Java编译器的测试

嵌入式Java编译器是基于C++语言开发的,在实现上主要包括两部分:用户界面和编译器。在用户界面方面其表现为一个图形化继承开发环境,所有的代码编辑和编译都可在该环境中完成,将给出开发平台的新建Java应用工程的过程,如图4所示。

图4 开发平台的新建工程界面

对移植的结果进行测试非常重要,但是目前缺少全面有效的测试方法,本文采用的是用模拟器和实际测试版相结合的测试方法。采用模拟器可以大大地降低测试的难度,提高效率,更重要的是可以进行跟踪调试以帮助找到错误。同时,为了测试实时Java编译器的正确性,即测试实时Java编译器输出的MCS-51系列单片机汇编程序是否正确,在实际测试板上运行输出的程序是非常必要的,只有真正在硬件机器上运行过的程序才能确保编译结果的正确性。测试结果分析,本文嵌入式Java编译器已经基本实现了从Java语言到MCS-51系列单片机汇编指令的编译。

结 语

[1] 滕海坤.基于RTSJ的智能家居系统网关设计[J].桂林理工大学学报,2011(1).

[2] 高爱玲.Java与C/C++的编译器优劣探讨[J].信息通信,2014(3).

[3] 沈健.基于嵌入式linux系统交叉编译的实现[J].赤子,2014(12).

[4] 朱磊.一种污水处理仿真语言编译器的实现与应用[J].软件,2014,35(2).

[5] John Levine. Flex与Bison (中文版)[M].陆军,译.南京: 东南大学出版社,2012.

[6] Jourdan J-H, Laporte V, Blazy S,et al. A formally-verified c static analyzer In: Proc. of the POPL2015[M]. Mumbai:ACM Press, 2015.

[7] 尚书.可信编译器L2C的核心编译步骤及其设计与实现[J].软件学报,2017,28(5).

[8] 周文.复杂系统建模仿真语言编译器的实现与应用[J].系统仿真学报,2016,28(7).

猜你喜欢
代码生成分析器词法
Lustre语言可信代码生成器研究进展
酒精分析器为什么能分辨人是否喝过酒
多边形电极线形离子阱质量分析器的结构与性能
分析化学(2018年12期)2018-01-22 12:31:46
应用于词法分析器的算法分析优化
谈对外汉语“词法词”教学
代码生成技术在软件开发中的应用
电子世界(2016年15期)2016-08-29 02:14:28
基于XML的代码自动生成工具
电子科技(2015年2期)2015-12-20 01:09:20
2010年高考英语“相似”考题例析
基于关系数据模型代码生成器的设计与实现
面向扩展文法语义分析器的自动生成