编译原理实验三:利用C语言编程实现TINY语言的词法分析器

#王者杯·14天创作挑战营·第1期#


一、实验目的

  1. 深入理解词法分析原理

    • 掌握词法分析器在编译流程中的作用(将字符流转换为有意义的词素)
    • 理解有限自动机(DFA/NFA)在词法分析中的实现方式
  2. 实践C语言实现技巧

    • 学习使用状态转换图实现词法分析
    • 掌握指针操作处理字符串输入
    • 熟悉文件I/O操作读取源代码
  3. TINY语言特性实践

    • 识别TINY语言的保留字(如if/else/repeat等)
    • 处理特殊符号(:=, <>, ±*/等复合运算符)

二、实验要求

  1. 预习报告内容要求

    • TINY语言词法规则表格(示例):
      词法单元正则模式
      标识符[a-zA-Z][a-zA-Z0-9]*
      数字[0-9]+
      赋值运算符“:=”
  2. C语言实现要求

    // 词法分析器核心函数示例
    TokenType getToken() {
        while (char c = getNextChar()) {
            switch(c) {
                case ':':
                    if (peekChar() == '=') {
                        consumeChar();
                        return ASSIGN;
                    }
                    break;
                // 其他case分支...
            }
        }
    }
    
  3. 测试程序规范

    • 测试案例需包含以下边界情况:
      x := 123;  { 数字赋值 }
      if x<>y then  { 不等号处理 }
      
  4. 实验报告格式

    • 必须包含词法错误处理日志示例:
      第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, ";")

四、拓展知识

  1. 自动生成技术对比

    实现方式优点缺点
    手工编写灵活可控开发效率低
    Lex/Flex开发快速黑盒调试困难
  2. 工业级词法分析优化

    • 使用双缓冲技术处理大文件
    • 哈希表加速关键字查找
    • 正则表达式引擎优化(如Thompson NFA算法)
  3. 现代编译器应用实例

    • GCC的预处理阶段词法处理
    • LLVM中clang的词法分析器架构
    • Java编译器javac的词法错误恢复机制
  4. 进阶学习建议

    • 阅读《编译原理》第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
在这里插入图片描述

七、运行效果

成功测试的结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梁辰兴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值