编译原理 实验一 词法分析之预处理
程序员文章站
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();
}
}