实验1. 词法分析程序的设计
实验要求就是对于一段代码,去掉空格注释压成一行,并且能识别出各种标识符和错误的语法。想要源码的私信,感谢点赞。
1.实验软件环境
开发平台:Windows 11
编程语言:C++
编译器版本:GCC 11.20 C++20
IDE:VS Code
2.实验原理描述
C++语言子集:
(1)关键字:
void、int、main、return、if、else
(2)运算符和界限符:
+ 、 − 、 ∗ 、 / 、 % 、 = 、 ; 、 ( 、 ) 、 [ 、 ] 、 { 、 } +、-、*、/、\%、=、;、(、)、[、]、\{、\} +、−、∗、/、%、=、;、(、)、[、]、{、}
(3) 整型常数(INT)和标识符(ID)通过正规文法定义
3.功能实现描述
删除注释空格函数:
把读进来的字符串处理压成一行,主要是要识别’\n’转义字符和\字符与*字符
// 处理字符串中的注释和空格
string ShanchuSpace(const string& rawstr) {
string str;
for (int i = 0, skipLine = 0, skipOverLine = 0; i < rawstr.size(); i++) {
char cur = rawstr[i];
// 处理注释//
if (skipLine) {
if (cur == '\n') {
skipLine = 0;
}
continue;
}
// 处理跨行注释/*
if (skipOverLine) {
if (cur == '/') {
if (i && rawstr[i - 1] == '*') {
skipOverLine = 0;
}
}
continue;
}
// 去除空格
if (cur == '=' || cur == '<' || cur == '>' || cur == '+' ||
cur == '-' || cur == '(' || cur == '{') {
while (str.size() && str.back() == ' ')
str.pop_back();
str += cur;
continue;
}
if (cur != '\n' && cur != '/' && cur != '*' && cur != ' ' &&
cur != '=') {
if (i && rawstr[i - 1] == '/')
str += '/';
if (i && rawstr[i - 1] == '*')
str += '*';
// 处理流运算符空格
if (cur == '<' && i && rawstr[i - 1] == '<') {
str.pop_back();
while (str.size() && str.back() == ' ')
str.pop_back();
str += '<';
}
str += cur;
continue;
}
// 处理多余空格 当前面的字符为字母或数字时 才加入空格
if (cur == ' ' && (isdigit(str.back()) || isalpha(str.back()))) {
str += cur;
continue;
}
// 当前字符为/时 前方字符为/时为注释 否则为除号
if (cur == '/') {
// 为注释符
if (i && rawstr[i - 1] == '/') {
skipLine = 1;
continue;
}
// 为除号
if (rawstr.size() == i - 1) {
while (str.size() && str.back() == ' ')
str.pop_back();
str += cur;
}
continue;
}
// 当前字符为*时 前方字符为/时为注释 否则为乘号
if (cur == '*') {
// 为注释符
if (i && rawstr[i - 1] == '/') {
skipOverLine = 1;
continue;
}
// 为乘号
if (rawstr.size() == i - 1) {
while (str.size() && str.back() == ' ')
str.pop_back();
str += cur;
}
}
}
return str;
}
work函数:
该函数就是把各个关键词和标识符找到,并且输入到文件中,并且定义了两个哈希表,一个用来看什么字符串对应什么数字,一个是检验这个字符串之前有没有出现过
// 记录出现过的标识符
map<string, int> used;
// 单词映射哈希表 标识符32 十进制整数33 八进制整数34 十六进制整数35
map<string, int> Hash{{"void", 1}, {"main", 2}, {"int", 3}, {"cout", 4},
{"return", 5}, {"if", 6}, {"else", 7}, {"break", 8},
{"for", 9}, {"(", 10}, {")", 11}, {"{", 12},
{"}", 13}, {"+", 14}, {"-", 15}, {"*", 16},
{"/", 17}, {"%", 18}, {"=", 19}, {",", 20},
{";", 21}, {"<<", 22}, {"<=", 23}, {">=", 24},
{"<", 25}, {">", 26}, {"==", 27}, {"[", 28},
{"]", 29}, {"++", 30}, {"--", 31}};
const int identityNum = 32, decimalNum = 33, octalNum = 34, hexNum = 35;
然后work函数主要就是遍历整个字符串,按照自己设计的状态转移图和有穷自动机的思路来模拟实现,然后输出到文件中,输出函数:
// 输出单词
void ShuChuWord(int num, int type, const string& s) {
cout << setw(4) << num << ' ' << setw(10) << type << ' ' << setw(12) << s
<< endl;
}
work函数:
void work() {
while (index < str.size()) {
char cur = str[index];
if (cur == ' ') {
index++;
continue;
}
string tmp;
// 查找关键词或者标识符
if (isalpha(cur) || cur == '_') {
while (index < str.size() &&
(isalnum(str[index]) || str[index] == '_'))
tmp += str[index++];
// 查找到关键词
if (Hash.contains(tmp)) {
ShuChuWord(cnt, Hash[tmp], tmp);
cnt++;
continue;
} // 找到标识符
else {
if (used.contains(tmp))
continue;
used[tmp] = cnt;
ShuChuWord(cnt, identityNum, tmp);
cnt++;
continue;
}
}
// 当前找到的是数字 则为整数 直到找到非数字
else if (isdigit(cur)) {
// 判断八进制和十六进制
bool hex = false, oct = false;
if (cur == '0') {
// 十六进制
if (index + 1 < str.size() && str[index + 1] == 'x') {
hex = true;
tmp += "0x";
index += 2;
} else if (index + 1 < str.size()) {
tmp += "0";
oct = true;
index++;
}
}
// 是否是非法标识符
bool flag = true;
while (index < str.size() &&
(isdigit(str[index]) || isalpha(str[index]) ||
str[index] == '_')) {
if (isalpha(str[index])) { // 如果有字符出现 视为违法标识符
flag = false;
}
tmp += str[index++];
}
if (!flag) {
if (used.contains(tmp))
continue;
used[tmp] = cnt;
ErrorIDword.emplace_back(tmp, cnt);
} else {
if (oct) {
ShuChuWord(cnt, octalNum, tmp);
} else if (hex) {
ShuChuWord(cnt, hexNum, tmp);
} else
ShuChuWord(cnt, decimalNum, tmp);
}
cnt++;
} // 其余符号开头 < > 开头特殊处理
else {
if (cur == '<') {
// 如果有=或<
if (index + 1 < str.size() &&
(str[index + 1] == '=' || str[index + 1] == '<')) {
string tmp;
tmp += cur;
tmp += str[index + 1];
ShuChuWord(cnt, Hash[tmp], tmp);
cnt++;
index++;
} else {
ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
cnt++;
}
} else if (cur == '>') {
if (index + 1 < str.size() && str[index + 1] == '=') {
string tmp{cur};
tmp += "=";
ShuChuWord(cnt, Hash[tmp], tmp);
cnt++;
index++;
} else {
ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
cnt++;
}
} else if (cur == '+' || cur == '-') { //++ -- 特殊处理
if (index + 1 < str.size() && str[index + 1] == cur) {
ShuChuWord(cnt, Hash[string(2, cur)], string(2, cur));
cnt++;
index += 2;
} else {
ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
cnt++;
index++;
}
} else {
ShuChuWord(cnt, Hash[string(1, cur)], string(1, cur));
cnt++;
}
index++;
}
}
}
然后还要把不合法的标识符输出出来
// 输出不合法标识符
void ShuChuErrorID(const string& rawstr, vector<pair<string, int>>& vec) {
int index = 0, column = 1, row = 1;
for (auto [str, pos] : vec) { //结构化绑定
while (index < rawstr.size()) {
char cur = rawstr[index];
if (cur == '\n')
row++, column = 1;
if (rawstr[index] == str[0] &&
rawstr.substr(index, str.size()) == str) {
index += str.size();
cout << "发现非法标识符:" << str << '\n';
cout << "位于源程序第" << pos << "个单词\n";
cout << "位于源程序第" << row << "行,第" << column << "列\n\n";
column += str.size();
break;
} else
index++, column++;
}
}
}
4.实验结果展示与分析
首先初始有 1. t x t 1.txt 1.txt文档,里面有我写的一段代码。
在运行后,在终端中输入文件路径,因为在同一个文件夹中,就只需要输入相对路径,输入好了按回车
运行好后,在左侧,多出了 S h u C h u . t x t 和 Y u C h u L i . t x t ShuChu.txt和YuChuLi.txt ShuChu.txt和YuChuLi.txt两个txt文件,代表运行成功了,在当前文件夹中生成了好了两个需要的txt文件。
可以看出,在 Y u C h u L i . t x t YuChuLi.txt YuChuLi.txt中,已经把代码过滤了空格和注释,全部压成一行。
在 S h u C h u . t x t ShuChu.txt ShuChu.txt中,根据预处理的结果识别的各种变量,标识符输出到这个文件里,在最后输出了非法标识符,查看后是因为定义了int类型,但用浮点数类型给它赋值。