一、实验目的
设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。
二、 实验要求
- 实现预处理功能
源程序中可能包含有对程序执行无意义的符号,要求将其剔除。
首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。 - 实现词法分析功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。其中,
syn为单词种别码。
Token为存放的单词自身字符串。
Sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。 - 待分析的C语言子集的词法(可以自行扩充,也可以按照C语言的词法定义)
1)关键字
main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。
2)运算符和界符
± * / : := < <> <= > >= = ; ( ) #
3)其他标记ID和NUM
通过以下正规式定义其他标记:
ID→letter(letter|digit)*
NUM→digit digit*
letter→a|…|z|A|…|Z
digit→0|…|9…
4)空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。 - 各种单词符号对应的种别码
表1 各种单词符号的种别码
单词符号 种别码 单词符号 种别码
main 1 ; 41
if 2 ( 42
then 3 ) 43
while 4 int 7
do 5 double 8
static 6 struct 9
ID 25 break 10
NUM 26 else 11
+ 27 long 12
- 28 switch 13
* 29 case 14
/ 30 typedef 15
: 31 char 16
:= 32 return 17
< 33 const 18
<> 34 float 19
<= 35 short 20
> 36 continue 21
>= 37 for 22
= 38 void 23
default 39 sizeof 24
do 40 # 0
- 词法分析程序的主要算法思想
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到的单词符号的第一个字符的种类,拼出相应的单词符号。 - 主程序示意图
主程序示意图如图1所示。
其中初值包括如下两方面:
(1). 关键字表初值
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表为一个字符串数组,其描述如下:
char *rwtab[27]={“main”,”if”,”then”,”while”,”do”,” static”,”int”,” double”,”struct”,”break”,”else”,”long”,”switch”,”case”,”typedef”,”char”,”return”,”const”,”float”,”short”,”continue”,”for”,”void”,”default”,”sizeof”,”do”};
(2). 程序中需要用到的主要变量:syn,token和sum。
2. 扫描子程序的算法思想
首先设置三个变量:token用来存放构成单词符号的字符串;sum用来存放整型单词;syn用来存放单词符号的种别编码。扫描子程序主要部分流程如图2所示。
package StringToJAVA;
import javafx.util.Pair;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
/**
* @author [25'h]
* @date 2022.10.22
* 实现:小数,标识符下划线处理,.符号,"号和字符串处理,
* 缺陷: 异或与非符号、移位符号、
* 说明: 标识符100以上就是自定义的变量,-1表示常量
*/
public class LexicalAnalysis {
static String[] keyWord = {"main", "if", "then", "while", "do", "static", "int", "double", "struct", "break", "else", "long", "switch", "case", "typedef", "char", "return", "const", "float", "short", "continue", "for", "void", "sizeof", "default"};
static String stringOfAll = "";//所有的预处理后的程序均在这
static final File file = new File("D:\\code_management\\algorithm\\src\\StringToJAVA\\test.txt");// 程序文件路径
static ArrayList<Pair<String, Integer>> resList = new ArrayList<>();//程序有续集
static HashMap<String, Integer> resMap = new HashMap<>();//字母集
static int myParamIndex = 100;//自定义标识符其实标号
public static void main(String[] args) {
if (!file.exists()) {
System.out.println(file.getAbsolutePath() + " 不存在");
} else {
System.out.println(file.getAbsolutePath() + " 已打开");
}
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
StringBuffer stringBuffer = new StringBuffer();
while ((stringOfAll = bufferedReader.readLine()) != null) {
stringBuffer.append(stringOfAll).append("\n");// 为了能去除单行注释,必须保留\n,StringBuffer::readLine()不包含\n
}
stringOfAll = stringBuffer.toString();
bufferedReader.close();
} catch (Exception e) {
e.printStackTrace();
}
preProcedure();
mainFunc();
for (Pair<String, Integer> pair : resList) {
System.out.println("< " + pair.getKey() + " : " + pair.getValue() + " >");
}
}
/**
* @param _s 分析出来的字符集
* @param _i 字符集本身的索引,只有是非自定义标识符才有效
* @param isMyParam 自定义标识符号会自己在100号后自动生成序号
*/
public static void flash(String _s, Integer _i, boolean isMyParam) {
if (resMap.containsKey(_s)) {
_i = resMap.get(_s);
} else {
if (!isMyParam) {
resMap.put(_s, _i);
} else {
resMap.put(_s, myParamIndex);
_i = myParamIndex++;
}
}
resList.add(new Pair<>(_s, _i));
}
/**
* 预处理程序
*/
public static void preProcedure() {
//单行注释
stringOfAll = stringOfAll.replaceAll("//.*?\n", " ");
// 回车
stringOfAll = stringOfAll.replaceAll("[\n\r\t]", " ");
// 多行注释-------------------------存在缺陷"/*这是字符串中的假多行注释*/"
stringOfAll = stringOfAll.replaceAll("/\\*.*?\\*/", " ");
// 多个空格
stringOfAll = stringOfAll.replaceAll(" +", " ");
System.out.println("预处理完成:");
}
public static void mainFunc() {
StringBuffer curStr;
int Len = stringOfAll.length(), syn;
for (int i = 0; i < Len; i++) {
while (i < Len && stringOfAll.charAt(i) == ' ') i++;
if (i >= Len) break;
curStr = new StringBuffer();
if (stringOfAll.charAt(i) == '.') {
flash(".", 48, false);
continue;
}
//是字母
if (isLetter(stringOfAll.charAt(i)) || stringOfAll.charAt(i) == '_') {
curStr.append(stringOfAll.charAt(i++));
while (i < Len && (isLetter(stringOfAll.charAt(i)) || isDigital(stringOfAll.charAt(i)) || stringOfAll.charAt(i) == '_')) {
curStr.append(stringOfAll.charAt(i++));
}
i--;
syn = whichKeyWord(curStr.toString());
if (syn == 25) {
flash(curStr.toString(), -1, true);
} else {
flash(curStr.toString(), syn, false);
}
//是数字
} else if (isDigital(stringOfAll.charAt(i))) {
//
curStr.append(stringOfAll.charAt(i++));
while (i < Len && (stringOfAll.charAt(i) == '.' || isLetter(stringOfAll.charAt(i)) || isDigital(stringOfAll.charAt(i)))) {
curStr.append(stringOfAll.charAt(i++));
}
i--;
boolean lab = true;
for (int j = 0; j < curStr.length(); j++) {
if (isLetter(curStr.charAt(j))) {
System.out.println("ERROR:" + curStr);
lab = false;
break;
}
}
if (lab) {
String temp = curStr.toString().replaceAll("\\.", "");
if (curStr.length() - temp.length() > 1) {
System.out.println("ERROR:" + curStr);
} else {
flash(curStr.toString(), -1, false);
}
}
} else if (((syn = isSingleChar(stringOfAll.charAt(i))) != -1)) {
flash(String.valueOf(stringOfAll.charAt(i)), syn, false);
} else if (stringOfAll.charAt(i) == '*') {
if (i + 1 < Len && stringOfAll.charAt(i + 1) == '*') {
flash("**", 31, false);
i++;
} else {
flash("*", 29, false);
}
} else if (stringOfAll.charAt(i) == '=') {
if (i + 1 < Len && stringOfAll.charAt(i + 1) == '=') {
flash("==", 32, false);
i++;
} else {
flash("=", 38, false);
}
}
//判断是否为<>或<=
else if (stringOfAll.charAt(i) == '<') {
if (i + 1 < Len && stringOfAll.charAt(i + 1) == '>') {
flash("<>", 34, false);
i++;
} else if (i + 1 < Len && stringOfAll.charAt(i + 1) == '=') {
flash("<=", 35, false);
} else {
flash("<", 33, false);
}
}
//判断是否为>=
else if (stringOfAll.charAt(i) == '>') {
if (stringOfAll.charAt(i + 1) == '=') {
flash(">=", 37, false);
i++;
} else {
flash(">", 36, false);
}
//处理字符串
} else if (stringOfAll.charAt(i) == '"') {
curStr.append('"');
i++;
while (i < Len && stringOfAll.charAt(i) != '"') {
curStr.append(stringOfAll.charAt(i++));
}
curStr.append('"');
i++;
flash(curStr.toString(), -1, false);
}
}
}
//-----------------------------------!|^&
private static int isSingleChar(char charAt) {
switch (charAt) {
case '+':
return 27;
case '-':
return 28;
case '/':
return 30;
case '[':
return 39;
case ']':
return 40;
case ';':
return 41;
case '(':
return 42;
case ')':
return 43;
case '#':
return 0;
default:
return -1;
}
}
private static boolean isLetter(char charAt) {
if (charAt >= 65 && charAt <= 90) return true;
else return charAt >= 97 && charAt <= 122;
}
public static boolean isDigital(char c) {
return c >= 48 && c <= 57;
}
public static int whichKeyWord(String temp) {
for (int i = 0; i < 25; i++) {
if (keyWord[i].equals(temp)) {
if (i == 24) return 41;
return i + 1;
}
}
return 25;
}
}
test.txt文件:
#include<stdio.h>
int main{
/*
这他喵的是多行注释
*/
double _a_ = 4.33253;
//这是单行注释
int _ = 5;
for(int i = 0; i < a; i++){
printf("Hello C");
}
return 0;
}
运行结果:
D:\code_management\algorithm\src\StringToJAVA\test.txt 已打开
预处理完成:
< # : 0 >
< include : 100 >
< < : 33 >
< stdio : 101 >
< . : 48 >
< h : 102 >
< > : 36 >
< int : 7 >
< main : 1 >
< double : 8 >
< _a_ : 103 >
< = : 38 >
< 4.33253 : -1 >
< ; : 41 >
< int : 7 >
< _ : 104 >
< = : 38 >
< 5 : -1 >
< ; : 41 >
< for : 22 >
< ( : 42 >
< int : 7 >
< i : 105 >
< = : 38 >
< 0 : -1 >
< ; : 41 >
< i : 105 >
< < : 33 >
< a : 106 >
< ; : 41 >
< i : 105 >
< + : 27 >
< + : 27 >
< ) : 43 >
< printf : 107 >
< ( : 42 >
< "Hello C" : -1 >
< ; : 41 >
< return : 17 >
< 0 : -1 >
< ; : 41 >