已开启
提交第2次作业语法分析实验-杨泽政-43 #34
limn-创建于 2025年12月19日
提交第2次作业语法分析实验-杨泽政-43 #34
已开启
limn-创建于 2025年12月19日
13 个文件变更+2148-0
Ayangzezheng-2025-parse/correct.txt+32-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/in.txt+32-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/lexer.h+263-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/main.cpp+66-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/out.txt+128-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/parser.h+1253-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/readme.txt+5-0文件内容审核中,请稍后刷新重试
Ayangzezheng-2025-parse/wrong.txt+33-0文件内容审核中,请稍后刷新重试
Binary files do not support preview
@@ -0,0 +1,11 @@
1+// 基础语法测试
2+func main() {
3+ var num1:int = 8
4+ while(num1 >=0){
5+ if(num1 == 2) break
6+ }
7+ let 4um2 = @
8+}
9+/*闭合
10+的多行注释*/
11+/*未闭合的多行注释
@@ -0,0 +1,39 @@
1+========================================
2+仓颉轻量级词法分析结果
3+========================================
4+单词类型 | 单词值
5+========================================
6+func | func
7+main | main
8+界符 | (
9+界符 | )
10+界符 | {
11+var | var
12+ID | num1
13+界符 | :
14+int | int
15+运算符 | =
16+NUM | 8
17+while | while
18+界符 | (
19+ID | num1
20+运算符 | >=
21+NUM | 0
22+界符 | )
23+界符 | {
24+if | if
25+界符 | (
26+ID | num1
27+运算符 | ==
28+NUM | 2
29+界符 | )
30+break | break
31+界符 | }
32+let | let
33+Error: 非法标识符(以数字开头),行号:7, 内容:4um2
34+运算符 | =
35+Error: 非法字符 '@',行号:7
36+界符 | }
37+Error: 未闭合的多行注释,行号:11
38+========================================
39+词法分析完成!共处理 11 行代码
@@ -0,0 +1,3 @@
1+in.txt为输入文件
2+out.txt文件保存词法分析结果
3+test1.cpp为词法分析源文件代码
@@ -0,0 +1,283 @@
1+#include <stdio.h>
2+#include <string.h>
3+#include <ctype.h>
4+ 
5+// 全局变量:行号(用于错误定位)
6+int line_num = 1;
7+char current_char; // 当前读取的字符
8+ 
9+// 关键字列表(按字典序排序,用于折半二分查找)
10+char *keywords[] = {
11+ "as", "bool" , "break", "case", "const", "continue", "do",
12+ "else", "float" ,"for", "from", "func", "if", "in", "int",
13+ "let", "main", "match", "var", "where", "while"
14+};
15+int keyword_count = sizeof(keywords) / sizeof(keywords[0]);
16+ 
17+/*
18+ 二分找:判断字符串是否为关键字
19+ str 待判断字符串
20+ return 1-是关键字,0-不是
21+ */
22+int is_keyword(char *str) {
23+ int left = 0, right = keyword_count - 1;
24+ while (left <= right) {
25+ int mid = (left + right) / 2;
26+ int cmp = strcmp(str, keywords[mid]);
27+ if (cmp == 0) return 1; // 找到关键字
28+ else if (cmp < 0) right = mid - 1;
29+ else left = mid + 1;
30+ }
31+ return 0;
32+}
33+ 
34+/*
35+ 读取下一个字符,更新行号和列号
36+ fp 源文件指针
37+ return 当前读取的字符(EOF表示文件结束)
38+ */
39+
40+int get_char(FILE *fp) {
41+ current_char = fgetc(fp);
42+ if (current_char == '\n') { // 换行:行号+1
43+ line_num++;
44+
45+ }
46+ return current_char;
47+}
48+ 
49+/*
50+ 跳过空白字符(空格、制表符、换行符等)
51+ fp 源文件指针
52+ */
53+void skip_whitespace(FILE *fp) {
54+ while (current_char != EOF && isspace(current_char)) {
55+ get_char(fp);
56+ }
57+}
58+ 
59+/*
60+ 处理注释(单行//和多行/* *\/)
61+ fp 源文件指针
62+ out 目标文件指针
63+ */
64+bool handle_comment(FILE *fp ,FILE *out) {
65+ bool flag = 0;
66+
67+ if (current_char == '/') {
68+ get_char(fp); // 读取注释起始符后的字符
69+ // 单行注释:跳过到行尾
70+ while (current_char != EOF && current_char != '\n') {
71+ get_char(fp);
72+ }
73+ } else if (current_char == '*') {
74+ // 多行注释:跳过到*/
75+ get_char(fp);
76+ while (current_char != EOF) {
77+ if (current_char == '*') {
78+ get_char(fp);
79+ if (current_char == '/') { // 找到注释结束符
80+
81+ break;
82+ }
83+ } else {
84+ get_char(fp);
85+ }
86+ }
87+ if (current_char == EOF) { // 未闭合的多行注释
88+ fprintf(stderr, "\033[31mError: 未闭合的多行注释,行号:%d \033[0m\n", line_num);
89+ fprintf(out, "Error: 未闭合的多行注释,行号:%d \n", line_num);
90+ printf("%c" , current_char);
91+ }
92+ } else {
93+ flag = 1;
94+ // 不是注释,是/运算符:回退字符和列号
95+ if(current_char != '\n')ungetc(current_char, fp);
96+
97+ current_char = '/';
98+ }
99+ return flag;
100+}
101+ 
102+/*
103+读取标识符/关键字(字母或_开头,后接字母/数字/_)
104+fp 源文件指针
105+out 目标文件指针
106+first_char 第一个字符(字母)
107+return 读取到的标识符字符串
108+ */
109+char *read_identifier(FILE *fp, char first_char) {
110+ static char buf[1024]; // 存储标识符(假设长度不超过1023)
111+ int i = 0;
112+ buf[i++] = first_char;
113+
114+ get_char(fp);
115+ // 继续读取字母、数字或下划线
116+ while (current_char != EOF && (isalnum(current_char) || current_char == '_')) {
117+ buf[i++] = current_char;
118+ get_char(fp);
119+ }
120+ buf[i] = '\0';
121+
122+ // 回退非标识符字符
123+ if (current_char != EOF && current_char != '\n') {
124+ ungetc(current_char, fp);//回退该字符到输入流
125+
126+ }
127+ return buf;
128+}
129+ 
130+/*
131+ 读取无符号整数(纯数字序列)
132+ fp 源文件指针
133+ out 目标文件指针
134+ first_char 第一个字符(数字)
135+ return 读取到的数字字符串(NULL表示非法)
136+ */
137+char *read_number(FILE *fp, char first_char , FILE *out) {
138+ static char buf[1024]; // 存储数字(假设长度不超过1023)
139+ int i = 0;
140+ buf[i++] = first_char;
141+
142+ get_char(fp);
143+ // 继续读取数字
144+ while (current_char != EOF && isdigit(current_char)) {
145+ buf[i++] = current_char;
146+ get_char(fp);
147+ }
148+ buf[i] = '\0';
149+
150+ // 检查是否以数字开头后接字母(非法标识符)
151+ if (current_char != EOF && isalpha(current_char)) {
152+
153+ // 跳过后续字符
154+ while( isalpha(current_char) || isdigit(current_char) || current_char == '_' ) {
155+
156+ buf[i++] = current_char;
157+ get_char(fp);
158+ }
159+ buf[i] = '\0';
160+ fprintf(stderr, "\033[31mError: 非法标识符(以数字开头),行号:%d, 内容:%s\033[0m\n",
161+ line_num, buf);
162+ fprintf(out, "Error: 非法标识符(以数字开头),行号:%d, 内容:%s \n",
163+ line_num, buf);
164+ if(current_char != '\n')ungetc(current_char,fp);
165+ return NULL;
166+ }
167+
168+ // 回退非数字字符
169+ if (current_char != EOF && current_char != '\n') {
170+ ungetc(current_char, fp);
171+ }
172+ return buf;
173+}
174+ 
175+/*
176+ 词法分析主函数
177+ fp 源文件指针
178+ out 目标文件指针
179+ */
180+void lex_analysis(FILE *fp , FILE *out) {
181+ // 终端输出结果头部
182+ fprintf(out, "========================================\n");
183+ fprintf(out, "仓颉轻量级词法分析结果\n");
184+ fprintf(out, "========================================\n");
185+ fprintf(out, "%-8s | %-15s \n", "单词类型", "单词值");
186+ fprintf(out, "========================================\n");
187+ 
188+ current_char = ' ';
189+ while (current_char != EOF) {
190+ get_char(fp); //
191+ skip_whitespace(fp); // 跳过空白字符
192+ if (current_char == EOF) break;
193+
194+ // 1. 识别标识符/关键字(字母开头)
195+ if (isalpha(current_char) || current_char == '_') {//是否为字母 或_
196+
197+ char *id = read_identifier(fp, current_char);
198+ if (is_keyword(id)) {
199+ fprintf(out, "%-8s | %-15s \n", id, id);
200+ } else {
201+ fprintf(out, "%-8s | %-15s \n", "ID", id);
202+ }
203+ }
204+ // 2. 识别无符号整数(数字开头)
205+ else if (isdigit(current_char)) {
206+
207+ char *num = read_number(fp, current_char , out);
208+ if (num != NULL) {
209+ fprintf(out, "%-8s | %-15s \n", "NUM", num);
210+ }
211+ }
212+ // 3. 识别运算符(+、-、*、/、>、<、!、=、>=、<=、!=、==)
213+ else if (current_char == '+' || current_char == '-' || current_char == '*' ||
214+ current_char == '/' || current_char == '>' || current_char == '<' ||
215+ current_char == '!' || current_char == '=') {
216+
217+ char op1 = current_char;
218+ get_char(fp);
219+
220+ // 处理/:可能是注释或运算符
221+ if (op1 == '/') {
222+ bool is_div = handle_comment(fp,out);
223+ if (is_div) { // 确认为/运算符
224+ fprintf(out, "%-8s | %-15c \n", "运算符", op1);
225+ //get_char(fp);
226+ }
227+ }
228+ // 严格处理双字符运算符:仅当连续两个字符匹配时识别
229+ else if ((op1 == '>' && current_char == '=') ||
230+ (op1 == '<' && current_char == '=') ||
231+ (op1 == '!' && current_char == '=') ||
232+ (op1 == '=' && current_char == '=')) {
233+ char op_str[3] = {op1, current_char, '\0'}; // 显式创建双字符运算符字符串
234+ fprintf(out, "%-8s | %-15s \n", "运算符", op_str);
235+ // get_char(fp);
236+ }
237+ // 单字符运算符(包括=):直接输出并回退(避免重复识别)
238+ else {
239+ fprintf(out, "%-8s | %-15c \n", "运算符", op1);
240+ if(current_char != '\n')ungetc(current_char , fp);
241+ }
242+ }
243+ // 4. 识别界符(()、{}、[]、,、;、:)
244+ else if (current_char == '(' || current_char == ')' || current_char == '{' ||
245+ current_char == '}' || current_char == '[' || current_char == ']' ||
246+ current_char == ',' || current_char == ';' || current_char == ':') {
247+ fprintf(out, "%-8s | %-15c \n", "界符", current_char);
248+ // get_char(fp);
249+ }
250+ // 5. 非法字符与单词
251+ else {
252+
253+ fprintf(stderr, "\033[31mError: 非法字符 '%c',行号:%d \033[0m\n", current_char, line_num);
254+ fprintf(out, "Error: 非法字符 '%c',行号:%d \n", current_char, line_num);
255+ }
256+
257+
258+ }
259+ 
260+ // 输出分析结束标记
261+ fprintf(out, "========================================\n");
262+ fprintf(out, "词法分析完成!共处理 %d 行代码\n", line_num );
263+}
264+ 
265+int main() {
266+ // 固定输入文件路径:当前目录下的 "in.txt"
267+ const char *in_path = "in.txt";
268+ const char *out_path = "out.txt";
269+ // 打开源文件(只读)
270+ FILE *in_fp = fopen(in_path, "r");
271+ FILE *out_fp = fopen(out_path, "w");
272+ if (in_fp == NULL) {
273+ fprintf(stderr, "\033[31m打开文件失败!请确保当前目录下存在 '%s' 文件(需写入仓颉代码)\033[0m\n", in_path);
274+ return 1;
275+ }
276+ 
277+ // 执行词法分析(结果直接输出到终端)
278+ lex_analysis(in_fp , out_fp);
279+ 
280+ // 关闭源文件
281+ fclose(in_fp);
282+ return 0;
283+}