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

来源:互联网 发布:投资域名后悔死了 编辑:程序博客网 时间:2024/05/17 07:47

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


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

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



0 0
原创粉丝点击