要求:
任务描述
本关任务:使用C语言编写一个对C语言进行词法分析的程序。
编程要求
根据提示,在右侧编辑器补充代码,从标准输入流读取字符,并完成词法分析任务。
其中,单词可以分为以下几类
第1类:标识符,由字母开头,由字母和数字组成
第2类:保留字,共32个,已存储到keyword_list数组中
第3类:无符号整数,如123【不考虑科学记数法】
第4类,无符号实数,如123.456【不考虑科学记数法】
第5类,操作符,包括以下符号
算术运算符:+,-,,/,%,++,--
关系运算符:>,<,==,>=,<=,!=,
赋值运算符: =
逻辑运算符: &&,||,!
其他运算符: &(返回变量地址),(指向一个变量)
第6类,分隔符,包括:
, ; ( ) [ ] { } ' "
第7类,字符串,由" "包起来的字符(不包括"),如"abcde"中的abcde
测试说明
平台会对你编写的代码进行测试:
输入的数据为一段C语言代码,包括了上述所涉及到的单词,代码以#符号作为输入结束的标志。
输出的数据为一个二元组,包括单词的名称和单词的类别例如:
(sum,1)
(int,2)
(123,3)
(123.456,4)
(++,5)
(;,6)
(sum:%d,7)
样例测试输入:printf("hello world")#
预期输出:
(printf,1)
((,6)
(",6)
(hello world,7)
(",6)
(),6)
(;,6)
代码:
//
// main.c
// LexiAnalyze
//
// Created by steve xiaohu zhao on 2025/4/23.
//
#include <ctype.h>
#include <stdio.h>
#include <string.h>
// 定义 C 语言关键字列表
const char *keyword_list[] = {
"auto", "break", "case", "char", "const", "continue",
"default", "do", "double", "else", "enum", "extern",
"float", "for", "goto", "if", "int", "long",
"register", "return", "short", "signed", "sizeof", "static",
"struct", "switch", "typedef", "unsigned", "union", "void",
"volatile", "while"};
// 定义关键字数量
#define KEYWORD_COUNT 32
// 定义最大令牌长度
#define MAX_TOKEN 256
// 词法分析函数, 读取输入并进行令牌分类
void readsym(void) {
char ch; // 当前读取的字符
char token[MAX_TOKEN]; // 存储当前令牌的字符数组
int pos = 0; // 令牌字符数组的当前位置
// 循环读取字符, 直到遇到 '#'
while ((ch = getchar()) != '#') {
// 跳过空白字符(空格、换行、tab等)
if (isspace(ch)) {
continue;
}
pos = 0; // 重置令牌位置
token[0] = '\0'; // 初始化令牌为空字符串
// 1. 处理 标识符 or 保留字
if (isalpha(ch) || ch == '_') {
token[pos++] = ch; // 记录首字符
// 继续读取字母、数字或下划线, 组成标识符
while (((ch = getchar()) && (isalnum(ch))) || ch == '_') {
token[pos++] = ch;
}
token[pos] = '\0'; // 结束令牌字符串
ungetc(ch, stdin); // 将非标识符字符放回输入流
// 检查是否为保留字
int is_keyword = 0;
for (int i = 0; i < KEYWORD_COUNT; i++) {
if (strcmp(token, keyword_list[i]) == 0) {
// 如果是保留字, 输出格式为(令牌, 2)
printf("(%s,2)\n", token);
is_keyword = 1;
break;
}
}
// 如果不是保留字, 则为标识符, 输入格式为(令牌, 1)
if (!is_keyword) {
printf("(%s,1)\n", token);
}
} else if (isdigit(ch)) {
// 2. 处理无符号整数 or 无符号实数
token[pos++] = ch; // 记录首字符
int is_float = 0; // 标记是否为实数(包含小数点)
// 继续读取数字或小数点
while (((ch = getchar()) && isdigit(ch)) || ch == '.') {
if (ch == '.') {
if (is_float) {
break; // 表示遇到多个小数点, 退出循环
}
is_float = 1; // 标记为实数
}
token[pos++] = ch; // 记录字符
}
token[pos] = '\0'; // 结束令牌字符串
ungetc(ch, stdin); // 将非数字字符放回输入流
// 根据是否包含小数点, 输出 (令牌, 3) or (令牌, 4)
printf("(%s,%d)\n", token, is_float ? 4 : 3);
} else if (ch == '"') {
// 先处理 " 分隔符
printf("(\",6)\n");
// 3. 处理字符串
// 读取字符串内容, 直到遇到结束引号或终止符
while ((ch = getchar()) && ch != '"' && ch != '#') {
token[pos++] = ch;
}
token[pos] = '\0'; // 结束令牌字符串
if (ch == '"') {
// 正常结束的字符串, 输出 (令牌, 7)
printf("(%s,7)\n", token);
// 字符串结束前先处理分隔符
printf("(\",6)\n");
} else {
// 未闭合的字符串, 仍然输出 (令牌, 7),并退出
printf("(%s,7)\n", token);
break;
}
} else {
// 处理 操作符 or 分隔符
token[pos++] = ch; // 记录当前字符
token[pos] = '\0';
// 检查是否构成双字符操作符(如 ++, --, >= 等)
if (ch == '+' || ch == '-' || ch == '>' || ch == '<' || ch == '=' ||
ch == '!' || ch == '&' || ch == '|') {
// 继续读取一个字符
ch = getchar();
// 判断是否构成双字符操作符
if ((token[0] == '+' && ch == '+') ||
(token[0] == '-' && ch == '-') ||
(token[0] == '&' && ch == '&') ||
(token[0] == '|' && ch == '|') ||
(ch == '=' && ((token[0] == '>') || (token[0] == '<') ||
(token[0] == '!')))) {
// 记录第二个字符
token[pos++] = ch; // 记录第二个字符
token[pos] = '\0'; // 结束令牌字符串
} else {
ungetc(ch, stdin); // 非双字符操作符,放回输入流
}
}
// 判断是操作符还是分隔符
if (strchr("+-*/%=><!&", token[0]) || strcmp(token, "++") == 0 ||
strcmp(token, "--") == 0 || strcmp(token, "==") == 0 ||
strcmp(token, ">=") == 0 || strcmp(token, "<=") == 0 ||
strcmp(token, "!=") == 0 || strcmp(token, "&&") == 0 ||
strcmp(token, "||") == 0) {
// 表示是操作符,输出 (令牌, 5)
printf("(%s,5)\n", token);
} else if (strchr(",;()[]{}'\"", token[0])) {
// 表示是分隔符,输出 (令牌, 6)
printf("(%s,6)\n", token);
}
}
}
}
int main(int argc, const char *argv[]) {
// 调用读取下一个字符函数, 进行词法分析
readsym();
return 0;
}
/*
测试输入1:
int num1,num2,sum;
sum = add(num1,num2);
printf("sum:%d",sum);
#
预期输出:
(int,2)
(num1,1)
(,,6)
(num2,1)
(,,6)
(sum,1)
(;,6)
(sum,1)
(=,5)
(add,1)
((,6)
(num1,1)
(,,6)
(num2,1)
(),6)
(;,6)
(printf,1)
((,6)
(",6)
(sum:%d,7)
(",6)
(,,6)
(sum,1)
(),6)
(;,6)
测试输入2:
a=2*sin(b)+exp(b)/5+12.34;
#
预期输出:
(a,1)
(=,5)
(2,3)
(*,5)
(sin,1)
((,6)
(b,1)
(),6)
(+,5)
(exp,1)
((,6)
(b,1)
(),6)
(/,5)
(5,3)
(+,5)
(12.34,4)
(;,6)
测试输入3:
for(i=0;i<=100;i++)
{sum = sum + i;}
#
预期输出:
(for,2)
((,6)
(i,1)
(=,5)
(0,3)
(;,6)
(i,1)
(<=,5)
(100,3)
(;,6)
(i,1)
(++,5)
(),6)
({,6)
(sum,1)
(=,5)
(sum,1)
(+,5)
(i,1)
(;,6)
(},6)
测试输入4:
int var=10;
int *p;
p=&var;
printf("var变量的地址是:%p",p);
#
预期输出:
(int,2)
(var,1)
(=,5)
(10,3)
(;,6)
(int,2)
(*,5)
(p,1)
(;,6)
(p,1)
(=,5)
(&,5)
(var,1)
(;,6)
(printf,1)
((,6)
(",6)
(var变量的地址是:%p,7)
(",6)
(,,6)
(p,1)
(),6)
(;,6)
*/
浙公网安备 33010602011771号