- 简单的扫描器设计
【问题描述】
熟悉并实现一个简单的扫描器,设计扫描器的自动机;设计翻译、生成Token的算法;编写代码并上机调试运行通过。
要求扫描器可识别的单词包括:关键字、界符、标识符和常整形数。
其中关键字表、界符表、标识符表、常整数表如下:(表中没有的关键字、界符等可以接着编号继续扩展)
【输入形式】源程序文件
【输出形式】
-
相应单词的Token序列;
-
标识符表,常数表。
【样例输入】
x10=x+y1*120+10;
【样例输出】
注意每行输出最后没有多余空格,最后一行输出后不换行。
Token :(I 1)(P 11)(I 2)(P 8)(I 3)(P 9)(C 1)(P 8)(C 2)(P 13) I :x10 x y1 C :120 10
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *K[] = {"", "int", "void", "break", "float", "while", "do", "struct", "const", "case", "for", "return", "if", "default", "else"};
char *P[] = {"", "-", "/", "(", ")", "==", "<=", "<", "+", "*", ">", "=", ",", ";", "++", "{", "}"};
char *I[100]; // 标识符表
char *C[100]; // 常数表
int num_I = 0; // 标识符表计数器
int num_C = 0; // 常数表计数器
// 判断字符是否为字母
int is_alpha(char ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
// 判断字符是否为数字
int is_digit(char ch) {
return (ch >= '0' && ch <= '9');
}
// 判断字符串是否为关键字
int is_keyword(char *str) {
for (int i = 1; i < sizeof(K) / sizeof(K[0]); ++i) {
if (strcmp(str, K[i]) == 0) {
return i; // 返回关键字在表中的索引
}
}
return 0;
}
// 判断标识符是否已经存在于标识符表中
int is_identifier_exists(char *str) {
for (int i = 0; i < num_I; ++i) {
if (strcmp(str, I[i]) == 0) {
return 1;
}
}
return 0;
}
// 判断常数是否已经存在于常数表中
int is_constant_exists(char *str) {
for (int i = 0; i < num_C; ++i) {
if (strcmp(str, C[i]) == 0) {
return 1;
}
}
return 0;
}
// 输出 Token
void output_token(char *type, int index) {
printf("(%s %d)", type, index);
}
// 扫描器函数,生成 Token
void scanner(char *input) {
int i = 0;
char token[100] = ""; // 用于存储单词或符号
while (input[i] != '\0') {
if (is_alpha(input[i])) { // 字母
while (is_alpha(input[i]) || is_digit(input[i])) {
strncat(token, &input[i], 1);
i++;
}
int keyword_index = is_keyword(token);
if (keyword_index != 0) {
output_token("K", keyword_index); // 关键字
} else {
if (!is_identifier_exists(token)) { // 如果标识符不存在于表中
I[num_I] = strdup(token); // 标识符
if (I[num_I] == NULL) {
printf("Memory allocation failed for identifier.\n");
exit(1);
}
num_I++;
}
// 输出标识符在表中的索引
for (int j = 0; j < num_I; j++) {
if (strcmp(token, I[j]) == 0) {
output_token("I", 1+j);
break;
}
}
}
memset(token, 0, sizeof(token));
} else if (is_digit(input[i])) { // 数字
while (is_digit(input[i])) {
strncat(token, &input[i], 1);
i++;
}
if (!is_constant_exists(token)) { // 如果常数不存在于表中
C[num_C] = strdup(token); // 常数
if (C[num_C] == NULL) {
printf("Memory allocation failed for constant.\n");
exit(1);
}
num_C++;
}
output_token("C", num_C); // 输出常数在表中的索引
memset(token, 0, sizeof(token));
} else if (input[i] == ' ' || input[i] == '\t' || input[i] == '\n') { // 空白字符
i++;
} else { // 运算符或界符
for (int j = 1; j < sizeof(P) / sizeof(P[0]); ++j) {
if (strncmp(&input[i], P[j], strlen(P[j])) == 0) {
output_token("P", j); // 输出运算符或界符在表中的索引
i += strlen(P[j]); // 更新 i 的位置
break;
}
}
}
}
}
int main() {
char input[100];
fgets(input, sizeof(input), stdin);
printf("Token :");
scanner(input);
printf("\nI :");
for (int i = 0; i < num_I; ++i) {
printf("%s", I[i]);
// 只有在不是最后一个元素时才打印空格
if (i < num_I - 1) {
printf(" ");
}
free(I[i]); // 释放标识符表的内存
}
// 输出C数组,最后不换行
printf("\nC :");
for (int i = 0; i < num_C; ++i) {
printf("%s", C[i]);
// 只有在不是最后一个元素时才打印空格
if (i < num_C - 1) {
printf(" ");
}
free(C[i]); // 释放常数表的内存
}
return 0;
}