您的位置:首页 > 编程语言 > Java开发

JAVA实现一个简单的代数运算语言编译器(三)--词法分析

2017-03-10 22:55 639 查看
上一篇文章我们为编译器编写了保留字、系统符号、出错提示等系统预制类,这一篇文章我们主要介绍编译器的词法分析部分。

我们首先创建一个名为WordAnalysis的类,为这个类编写一个共有静态方法 wordAnalysis 用来提供对外的词法分析接口。该方法接收一个字符串参数,即经过了分割的一个语句。返回一个字符串队列,即通过了词法分析并逐词分割后的语句,队列中的每一个字符串即为一个词,具体到这个项目中,词有可能是变量名、运算符号、赋值符号、保留字。

由于JAVA的字符类型char可以直接进行ASCII码的比较,因此这里我们可以编写对应的方法分别判断一个字符是字母、数字、空白符还是系统符号:

/*
* 判断是否是字母
* @param ch 需要判断的字符
* @return true代表是字母,false代表不是字母
*/
private static boolean isLetter(char ch) {
if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) {
return true;
}
return false;
}

/*
* 判断是否是数字
* @param ch 需要判断的字符
* @return true代表是数字,false代表不是数字
*/
private static boolean isDigit(char ch) {
if (ch >= 48 && ch <= 57) {
return true;
}
return false;
}

/*
* 判断是否是空格或换行
* @param ch 需要判断的字符
* @return true代表是空白符,false代表不是
*/
private static boolean isSpace(char ch) {
if (ch == 32 || ch == 10) {
return true;
}
return false;
}

/*
* 判断是否是小数点
* @param ch 需要判断的字符
* @return true代表是小数点,false代表不是
*/
private static boolean isPoint(char ch) {
if (ch == 46) {
return true;
}
return false;
}

/*
* 判断是否是系统符号
* @param ch 需要判断的字符
* @return true代表是系统符号,false代表不是
*/
private static boolean isSymbol(char ch) {
for (char symbol : Symbol.symbols) {
if (symbol == ch) {
return true;
}
}
return false;
}

接下来是词法分析的重点,怎样进行分析?我选择逐字符扫描输入的字符串,扫描到的字符可能是字母、数字、符号或者是空白符。这里有一个问题,我们在扫描一个字符的时候怎么知道上一个扫描到的字符是什么呢?如果我们扫描到了一个字母,而上一次扫描到的也是一个字母,那么包含这两个字符的就应当是属于某个变量名或者是保留字的;但如果上一次扫描到的字符是一个数字的话,则这两个字符应当就是属于某个变量名,而这个变量名如果恰好是以上一个数字开头,那么编译器就应当报错!因为项目规定变量名只能以字母开头。
有了以上分析,我选择用两个静态StringBuffer变量variableRegister、digitRegister和一个静态boolean变量anySpace来临时保存状态,这里我们不如就叫它们变量寄存器、数字寄存器以及空白寄存器吧。这里我们以语句 re= nu*2 来举例分析。

一开始变量寄存器和数字寄存器都是空的,空白寄存器值为false。

1.扫描到第一个字符r,判断其是一个字母,此时变量寄存器和数字寄存器都是空的,因此将其存入变量寄存器。

2.扫描到字符e,判断其是一个字母,而此时变量寄存器非空,数字寄存器是空的,因此将其存入变量寄存器。

3.扫描到字符=,判断其是系统符号,此时数字寄存器是空的,而变量寄存器值为re,因此将re添加到要返回的字符串队列当中,并将变量寄存器清空。

4.扫描到一个空白符,而此时变量寄存器和数字寄存器都是空的,因此不会有词法错误,仅仅将空白寄存器的值设为true.

5.扫描到字母n,此时变量寄存器和数字寄存器都是空的,因此将其存入变量寄存器,并将空白寄存器重新设为false.

6.扫描到字符u,判断其是一个字母,而此时变量寄存器非空,数字寄存器是空的,因此将其存入变量寄存器。

7.扫描到字符*,判断其是系统符号,此时数字寄存器是空的,而变量寄存器值为nu,因此将re添加到要返回的字符串队列当中,并将变量寄存器清空。

8.扫描到字符2,判断其是一个数字,此时变量寄存器和数字寄存器都是空的,因此将其存入数字寄存器。

9.字符串扫描完毕,此时变量寄存器是空的,数字寄存器中的值是2,因此将2添加到要返回的字符串队列当中,并将数字寄存器清空。

10.返回结果字符串队列。

以上就是对语句 re= nu*2 的一个词法分析以及按词分割的过程。而实际情况中可能会出现各种各样的词法错误,如变量名不规范,数字以小数点结尾、错误的空白符等等,对于这些情况,我都写在了wordAnalysis方法中,感兴趣的小伙伴可以仔细看一看。

WordAnalysis类的完整代码如下:

package com.liu.analysis;

import java.util.ArrayList;
import java.util.List;

import com.liu.system.Error;
import com.liu.system.MyException;
import com.liu.system.Symbol;

/*
* 词法分析类
* 创建于2017.3.8
* @author lyq
* */
public class WordAnalysis {

/* 变量名寄存字段,可临时寄存一个变量名 */
private static StringBuffer variableRegister = new StringBuffer();

/* 数字寄存字段,可临时寄存一个数字 */
private static StringBuffer digitRegister = new StringBuffer();

/* 是否存储有空白符 */
private static boolean anySpace = false;

/*
* 判断是否是字母
* @param ch 需要判断的字符
* @return true代表是字母,false代表不是字母
*/
private static boolean isLetter(char ch) {
if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) {
return true;
}
return false;
}

/*
* 判断是否是数字
* @param ch 需要判断的字符
* @return true代表是数字,false代表不是数字
*/
private static boolean isDigit(char ch) {
if (ch >= 48 && ch <= 57) {
return true;
}
return false;
}

/*
* 判断是否是空格或换行
* @param ch 需要判断的字符
* @return true代表是空白符,false代表不是
*/
private static boolean isSpace(char ch) {
if (ch == 32 || ch == 10) {
return true;
}
return false;
}

/*
* 判断是否是小数点
* @param ch 需要判断的字符
* @return true代表是小数点,false代表不是
*/
private static boolean isPoint(char ch) {
if (ch == 46) {

4000
return true;
}
return false;
}

/*
* 判断是否是系统符号
* @param ch 需要判断的字符
* @return true代表是系统符号,false代表不是
*/
private static boolean isSymbol(char ch) {
for (char symbol : Symbol.symbols) {
if (symbol == ch) {
return true;
}
}
return false;
}

/*
* 对一段输入的字符串进行词法分析
* @param str 需要分析的字符串
* @return 返回经过词法分析后的字符串队列
* @exception 数字前有空白符时出现异常
*/
public static List<String> wordAnalysis(String str) throws MyException {
//用来存放分析结果
List<String> result = new ArrayList<String>();

for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
// 是字母
if (isLetter(ch)) {
if (!variableRegister.toString().equals("")) {
// 字母-空白符-字母
if (anySpace) {
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.LETTER_SPACE_LETTER);
}
// 字母-字母
else {
variableRegister.append(ch);
continue;
}
}
if (!digitRegister.toString().equals("")) {
// 数字-空白符-字母
if (anySpace) {
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.NUMBER_SPACE_LETTER);
}
// 数字-字母
else {
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.LETTER_AFTER_NUMBER);
}
}
variableRegister.append(ch);
anySpace = false;
continue;
}
// 是数字
if (isDigit(ch)) {
if (!variableRegister.toString().equals("")) {
// 字母-空白符-数字
if (anySpace) {
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.LETTER_SPACE_NUMBER);
}
// 字母-数字
else {
variableRegister.append(ch);
continue;
}
}
if (!digitRegister.toString().equals("")) {
// 数字-空白符-数字
if (anySpace) {
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.NUMBER_SPACE_NUMBER);
}
// 数字-数字
else {
digitRegister.append(ch);
continue;
}
}
digitRegister.append(ch);
anySpace = false;
continue;
}
// 是空白符,记录出现了空格符,然后继续循环
if (isSpace(ch)) {
anySpace = true;
continue;
}
//是小数点
if(isPoint(ch)){
if(anySpace){
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.POINT_AFTER_SPACE);
}
else{
if(!variableRegister.toString().equals("")){
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.POINT_AFTER_LETTER);
}
if(!digitRegister.toString().equals("")){
if(digitRegister.toString().contains(String.valueOf(Symbol.point))){
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.POINT_AFTER_POINT);
}
digitRegister.append(ch);
continue;
}
}
}
//是系统符号
if(isSymbol(ch)){
anySpace = false;
//变量寄存器中存有变量
if(!variableRegister.toString().equals("")){
result.add(variableRegister.toString());
//清空变量寄存器
variableRegister.setLength(0);
}
//数字寄存器中存有数字
if(!digitRegister.toString().equals("")){
if(digitRegister.toString().endsWith(String.valueOf(Symbol.point))){
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.NUMBER_END_POINT);
}
result.add(digitRegister.toString());
//清空变量寄存器
digitRegister.setLength(0);
}
result.add(String.valueOf(ch));
continue;
}
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.CONTAIN_UNKNOWN_CAHR);
}
//变量寄存器中存有变量
if(!variableRegister.toString().equals("")){
result.add(variableRegister.toString());
//清空变量寄存器
variableRegister.setLength(0);
}
//数字寄存器中存有数字
if(!digitRegister.toString().equals("")){
if(digitRegister.toString().endsWith(String.valueOf(Symbol.point))){
variableRegister.setLength(0);
digitRegister.setLength(0);
throw new MyException(Error.NUMBER_END_POINT);
}
result.add(digitRegister.toString());
digitRegister.setLength(0);
}
return result;
}
}


以上就是整个的词法分析过程了,下一篇文章我们将介绍如何进行表达式的计算。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息