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

论坛 期权论坛 脚本     
匿名技术用户   2020-12-27 08:23   33   0

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


我们首先创建一个名为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 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP