利用C语言编程实现TINY语言的词法分析器
一、实验目的
-
深入理解词法分析原理
- 掌握词法分析器在编译流程中的作用(将字符流转换为有意义的词素)
- 理解有限自动机(DFA/NFA)在词法分析中的实现方式
-
实践C语言实现技巧
- 学习使用状态转换图实现词法分析
- 掌握指针操作处理字符串输入
- 熟悉文件I/O操作读取源代码
-
TINY语言特性实践
- 识别TINY语言的保留字(如if/else/repeat等)
- 处理特殊符号(:=, <>, ±*/等复合运算符)
二、实验要求
-
预习报告内容要求
- TINY语言词法规则表格(示例):
词法单元 正则模式 标识符 [a-zA-Z][a-zA-Z0-9]* 数字 [0-9]+ 赋值运算符 “:=”
- TINY语言词法规则表格(示例):
-
C语言实现要求
// 词法分析器核心函数示例 TokenType getToken() { while (char c = getNextChar()) { switch(c) { case ':': if (peekChar() == '=') { consumeChar(); return ASSIGN; } break; // 其他case分支... } } }
-
测试程序规范
- 测试案例需包含以下边界情况:
x := 123; { 数字赋值 } if x<>y then { 不等号处理 }
- 测试案例需包含以下边界情况:
-
实验报告格式
- 必须包含词法错误处理日志示例:
第5行:无法识别的字符 '@'
- 必须包含词法错误处理日志示例:
三、实验内容
1. TINY语言词法规则
- 保留字表:if/then/else/end/repeat/until/read/write
- 运算符分类:
- 算术运算符:+ - * /
- 比较运算符:= < > <>
- 特殊符号:; := ( )
2. 核心数据结构
typedef enum {
ENDFILE, ERROR,
IF, THEN, ELSE, /* 其他保留字... */
ID, NUM,
ASSIGN, LT, /* 其他运算符... */
} TokenType;
typedef struct {
TokenType type;
char lexeme[MAX_LEXEME];
int lineNo;
} Token;
3. 词法分析流程
4. 二元组输出示例
输入:
x := 42;
输出:
(ID, "x")
(ASSIGN, ":=")
(NUM, "42")
(SEMI, ";")
四、拓展知识
-
自动生成技术对比
实现方式 优点 缺点 手工编写 灵活可控 开发效率低 Lex/Flex 开发快速 黑盒调试困难 -
工业级词法分析优化
- 使用双缓冲技术处理大文件
- 哈希表加速关键字查找
- 正则表达式引擎优化(如Thompson NFA算法)
-
现代编译器应用实例
- GCC的预处理阶段词法处理
- LLVM中clang的词法分析器架构
- Java编译器javac的词法错误恢复机制
-
进阶学习建议
- 阅读《编译原理》第3章词法分析
- 研究Python的tokenize模块实现
- 尝试实现Unicode字符支持
五、示例代码
shiyan3.c
源代码
#include "stdio.h"
#include "ctype.h"
#include "string.h"
#define ADD 9
#define SUB 10
#define MUL 11
#define DIV 12
#define EQ 13
#define LT 14
#define LP 15
#define RP 16
#define FH 17
#define FZ 18
#define INT 19
#define ID 20
char TOKEN[20];
int lookup(char *str)
{
int i, c = 0;
char key[8][20] = {"if", "then", "else", "end", "repeat", "until", "read", "write"};
for (i = 0; i < 8; i++)
{
if (strcmp(key[i], str) == 0) c = i + 1;
}
return c;
}
void scanner_example(FILE *fp)
{
char ch;
int i, c;
fseek(fp, -1, 1);
ch = fgetc(fp);
if (isalpha(ch))
{
TOKEN[0] = ch;
ch = fgetc(fp);
i = 1;
while (isalpha(ch))
{
TOKEN[i] = ch;
i++;
ch = fgetc(fp);
}
fseek(fp, -1, 1);
TOKEN[i] = '\0';
c = lookup(TOKEN);
if (c == 0) printf("(%d,%s)\n", ID, TOKEN);
else printf("(%d,%s)\n", c, TOKEN);
}
else if (isdigit(ch))
{
TOKEN[0] = ch;
ch = fgetc(fp);
i = 1;
while (isdigit(ch))
{
TOKEN[i] = ch;
i++;
ch = fgetc(fp);
}
fseek(fp, -1, 1);
TOKEN[i] = '\0';
printf("(%d,%s)\n", INT, TOKEN);
}
else switch(ch)
{
case '+': printf("(%d,+)\n", ADD);
break;
case '-': printf("(%d,-)\n", SUB);
break;
case '*': printf("(%d,*)\n", MUL);
break;
case '/': printf("(%d,/)\n", DIV);
break;
case '=': printf("(%d,=)\n", EQ);
break;
case '<': printf("(%d,<)\n", LT);
break;
case '(': printf("(%d,()\n", LP);
break;
case ')': printf("(%d,))\n", RP);
break;
case ';': printf("(%d,;)\n", FH);
break;
case ':':
ch = fgetc(fp);
if (ch == '=') printf("(%d,:=)\n", FZ);
else fseek(fp, -1, 1);
break;
default: break;
}
return;
}
int main()
{
FILE *fp;
char ch;
if ((fp = fopen("test.txt", "r")) == NULL)
{
printf("can't open this file!\n");
return 1;
}
while ((ch = fgetc(fp)) != EOF) scanner_example(fp);
fclose(fp);
return 0;
}
六、操作步骤
1,在实验一目录下创建一个txt文件,并将文件名改为 shiyan3.c
。
2,将上述代码复制到 shiyan3.c
文件中(使用记事本打开)。
3,创建并编写测试文件 test.txt
。
if x<3
then y:=(x+3)*4
else y:=x-3
end
4,在实验三目录地址栏中输入cmd,按回车键,启动cmd命令窗口。
5,输入命令:gcc shiyan3.c
,生成可执行的词法分析程序。
6,产生了一个exe格式的词法分析程序——a.exe
。
7,输入命令:a.exe<test.txt
,测试我们创建并编写好的测试文件——test.txt
。
七、运行效果
成功测试的结果如下: