欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

编译原理 实验一 词法分析之预处理

程序员文章站 2022-06-02 17:07:06
...

编译原理   实验一   词法分析之预处理


1.实验要求:

PL/0语言(一种示例小语言)

编写预处理程序,其功能主要包括两部分:

(1)合并空白符:把原始程序中相邻的空格、制表符、回车等空白符合并成一个空格,便于后续处理;

(2)消除注释:消除原始程序中的注释内容;PL/0语法中没有规定注释的格式,参照Pascal语言规定如下两种注释格式:

  •  单行注释://”引导内容,与C++语言中单行注释一致。
  •  多行注释:(*”和“*)”之间内容,具体参见后面示例程序。
  • PL/0示例程序清单如下:
编译原理 实验一 词法分析之预处理

  • 预处理之后的效果
编译原理 实验一 词法分析之预处理


2.分析:

其实先去除多余空格也行,先去除注释也行。

不过,个人感觉,先去除注释比较方便,因为如果是一整行都是注释就直接舍去,不用再去除多余的空格了,省事。

判断注释:用String.indexof("") 方法来判断是否有注释和注释所在位置,如果注释出现在这一行的行首,则不用再去多余空格,直接舍去,如果是代码+注释,substring()提取子串即可。我用了两个flag标志注释的开始和结束。

去掉空格: 本来是正向思考,一直想方设法去除多余空格,回车,用下标前移和提取子串,但这样又麻烦又易出错,所以换了种方法,把符合条件的字符(非空格字符和非连续的空格)添加到StringBuilder中,再存入res数组中,简单很多。

【注意】:不要把类似于 int i=0; 中int 和 i 之间的空格去掉,去掉之后,程序的语意都改变了,就跟翻译英文的时候,把不同的单词给连在一起,那肯定改变了原来的意思。

3.代码(我也是初学,不过自己边查边敲,记录一下也供交流学习,欢迎给出建议)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Main {

	// res字符串数组存储经过处理之后的程序
	static int cnt = 0;
	static String[] res = new String[10000];
	// 标志注释的开始和结束
	static boolean flagBegin = false;
	static boolean flagEnd = false;

	public static void main(String[] args) {
		// 这里从指定路径读取文件,也可以由控制台输入文件路径和文件名
		File file = new File("D:\\plo.txt");
		BufferedReader reader = null;
		String temp = null;
		StringBuilder sb = new StringBuilder(); // 存储源文件
		try {
			reader = new BufferedReader(new FileReader(file));
			while ((temp = reader.readLine()) != null) { // 按行读入同时处理源文件
				sb.append(temp + "\n");
				res[cnt] = temp;
				compileFile(temp);
				// 不是注释,输出
				if (flagBegin == false && flagEnd == false) {
					System.out.println(res[cnt++]);
				}
				// 仍然是注释,且注释尚未结束不输出
				else if (flagBegin == true && flagEnd == false) {
					continue;
				}
				// 注释结束时,修改flag
				else if (flagBegin == true && flagEnd == true) {
					flagBegin = false;
					flagEnd = false;
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally { // 关闭文件读入流
			if (reader != null) {
				try {
					reader.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		// 输出源文件
		System.out.println("\n以下是源文件:\n");
		System.out.println(sb.toString());
	}

	// 处理多余空格,并删除注释
	private static void compileFile(String temp) {
		// 判断是否有注释,并获取注释的位置
		int index1 = temp.indexOf("//");
		int index2 = temp.indexOf("(*");
		int index3 = temp.indexOf("*)");
		// 如果一整行都是空格,直接跳过该行,判断下一行
		if (temp.length() == 0) {
			flagBegin = flagEnd = true;
			return;
		} else {
			if (index1 >= 0) {
				// 单行注释,分两种情况,一行都是注释和有代码有注释
				if (index1 == 0)
					flagBegin = flagEnd = true;
				else
					temp = temp.substring(0, index1);
			} else if (index2 >= 0)
				flagBegin = true;
			else if (index3 >= 0)
				flagEnd = true;
		}

        //去除空格,回车ASCII码13,换行ASCII码10,空格ASCII码32
		StringBuilder strb = new StringBuilder();
		char[] ch = new char[temp.length()];
		ch = temp.toCharArray();
		for (int i = 0; i < ch.length - 1; i++) {
			if (ch[i] != ' ') {
				strb.append(ch[i]);
			}
			if (ch[i] == ' ' && ch[i + 1] != ' ' && (i > 0)) {
				strb.append(ch[i]);
			}
		}
		// 上面的for循环,由于判断ch[i+1],当i=ch.length - 1时,i+1越界,所以单独判断最后一个字符
		if (ch[ch.length - 1] != ' ')
			strb.append(ch[ch.length - 1]);
		// 删除每一行开始的空格
		if (ch[0] == ' ')
			strb.deleteCharAt(0);
		res[cnt] = strb.toString();
	}
}