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

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


我们首先创建一个名为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) {
			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;
	}
}

以上就是整个的词法分析过程了,下一篇文章我们将介绍如何进行表达式的计算。



猜你喜欢

转载自blog.csdn.net/qq_33534383/article/details/61212283