0%

词法分析器

题目

  • 已知待分析的C语言子集的词法:
    • 关键字:main if else int while char 均为小写。
    • 专用符号:= + - * / < <= > >= == != ; , { } ( )
  • 实现对一个满足以上词法的分析。

算法流程图

首先在这记录一下我鸡汁的舍友找到了一个画流程图的在线网站,解救了一个用破解版Visio损害自己机子内正版Office一直在用PowerPoint画流程图的苦逼: https://www.processon.com/ 愿此平台越做越好(别倒闭,否则我就枯了~)

流程图

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//
// Created by ZhiNian on 2019/10/24.
//

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define ID 10 //变量种别码为10
#define NUM 20 //整型数字种别码为20


char key[6][10] = {"main", "int", "char", "if", "else", "while"};//通过二位数组定义标识符(这样数组下标+1便是对应的种别码)
char TOKEN[20];//储存每一个标识符或数字或符号的字符串

FILE *fp;//定义一个文件指针用来写入


int lookup(char *token) {//TOKEN的字符串在六个标识符中寻找
for (int i = 0; i < 6; ++i) {
if (!strcmp(token, key[i]))
return i + 1;//如果是就返回对应的种别码(数组下标+1便是种别码)
}
return 0;//若不是则返回0
}

void out(int id, char *token) {//格式化输出
printf("种别码:%d 单词符号:%s \n", id, token);//格式化在控制台输出
//以下是格式化输出写入文件
fputs("种别码:", fp);
fprintf(fp, "%d", id);
fputs(" 单词符号:", fp);
fputs(token, fp);
fputs("\n", fp);

}

void scanner(FILE *fp) {
char ch;
int i, c;
while (ch != EOF) {//ch是否是End Of File 即文件末尾,不是则一直循环扫描,是则退出
ch = fgetc(fp);//指针位置的字符读入ch
if (isalpha(ch)) {//判断此字符是否是英文
TOKEN[0] = ch;//是则将此字母写入TOKEN
ch = fgetc(fp);//再指向下一个字符传入ch
i = 1;//计数器+1
while (isalnum(ch)) {//判断字符变量c是否为字母或数字
TOKEN[i] = ch;//是则继续传入TOKEN
i++;//计数器+1
ch = fgetc(fp);//光标位置后移一个字节
}//单词识别结束
TOKEN[i] = '\0';//加上'\0'形成字符串
fseek(fp, -1, 1);//因为用了fgetc光标会立马后移,所以进行前移一位防止错过一个字符
c = lookup(TOKEN);//判断是否是关键词,是则返回相应种别码
if (c == 0)//返回c说明不是
out(ID, TOKEN);//那么传入种别码10,并传入TOKEN确定此关键词是变量名给out函数
else
out(c, TOKEN);//否则传入标识符的种别码,传入字符串给out函数
} else {//若不是字母开头
if (isdigit(ch)) {//判断是否是十进制数字字符
TOKEN[0] = ch;//同上写入TOKEN,直至不是十进制数字字符为止
ch = fgetc(fp);
i = 1;
while (isdigit(ch)) {
TOKEN[i] = ch;
i++;
ch = fgetc(fp);
}
TOKEN[i] = '\0';
fseek(fp, -1, 1);
out(NUM, TOKEN);//传入NUM的种别码和当前字符串给out函数
} else {//若既不是字母也不是数字
switch (ch) {//switch判断是否是以下符号,是则按对应符号传入种别码并传入字符串给out函数
case ' ':
break;
case '\n':
break;
case '\t':
break;
case EOF:
break;
case '+':
out(22, "+");
break;
case '-':
out(23, "-");
break;
case '*':
out(24, "*");
break;
case '/':
out(25, "/");
break;
case ';':
out(31, ";");
break;
case ',':
out(30, ",");
break;
case '{':
out(28, "{");
break;
case '}':
out(29, "}");
break;
case '(':
out(26, "(");
break;
case ')':
out(27, ")");
break;
case '<'://若是小于号
ch = fgetc(fp);//读取下一字符
if (ch == '=')out(35, "<=");//若下一位字符是=,则传入<=的种别码和字符串给out函数
else {//否则
fseek(fp, -1, 1);//回退
out(33, "<");//传入<的种别码和字符串给out函数
}
break;
case '>'://若是大于号
ch = fgetc(fp);//读取下一字符
if (ch == '=')out(34, ">=");//若下一位字符是=,则传入>=的种别码和字符串给out函数
else {//否则
fseek(fp, -1, 1);//回退
out(32, ">");//传入>的种别码字符串给out函数
}
break;
case '=':
ch = fgetc(fp);//若是等于号
if (ch == '=')out(36, "==");//读取下一字符若是=则传入==的种别码和字符串给out函数
else {//否则
fseek(fp, -1, 1);//回退
out(21, "=");//传入=的种别码和字符串给out函数
}
break;
case '!'://若是感叹号
ch = fgetc(fp);//读取下一字符
if (ch == '=')out(37, "!=");//若是=则传入!=的种别码和字符串给out函数
else out(-1, "Wrong!");//传入-1和"Wrong!"字符串给out函数,提示输入有误
break;
default://若是其他字符
out(-1, "Wrong!");//传入-1和"Wrong!"字符串给out函数,提示输入有误
break;
}
}
}
}
}

int main() {
FILE *fileP;//打开所需要读取分析词法的文件
fileP = fopen("D:\\OneDrive\\Personal\\OneDrive\\JetBrains\\CTemp\\Scanner\\test.txt", "r");//以只读方式打开
fp = fopen("D:\\OneDrive\\Personal\\OneDrive\\JetBrains\\CTemp\\Scanner\\Answer.txt", "w+");//打开可读写文件进行写入
scanner(fileP);//传入读取分析词法的文件
fclose(fp);//关闭文件
fclose(fileP);//关闭文件
return 0;
}

程序运行结果

1

总结

对于编程没啥总结的,因为感觉这个程序其实非常非常简单,相对于之前七七八八的算法题,但是令我收获最大的便是,原来编译器是这样工作的,当然到现在我们还没学完语法分析,从刚刚开始这个课的无聊,发现越来越有趣了,因为我在想,这是得有多聪明才能严谨地编出一个编译器,让程序对于每一个语句识别没有歧义。