四川大学编译原理实验包:词法语法分析器实战拆解
简介本资源是四川大学《编译原理》课程配套实验教学材料面向计算机专业本科生及编译技术初学者聚焦词法分析、语法分析、语义分析与代码生成四大核心环节的动手实践有效弥合理论学习与工程实现之间的鸿沟。压缩包共53个文件涵盖C语言源码c/c-文件、头文件h、测试用例txt/tny、Makefile构建脚本、实验说明文档docx、教学PPTpptx及README.md项目指南类型分布体现完整实验链路源码支撑模块开发测试用例验证正确性文档与PPT辅助理解设计逻辑与评分要求。目前已有157人下载学习。读者可直接复现实验环境逐周推进完成从DFA构造、递归下降解析、符号表管理到三地址码生成的全流程编译器组件开发并通过配套说明文档掌握实验目标、验收标准与常见问题应对策略为深入理解编译系统架构打下坚实实践基础。1. 四川大学编译原理实验包不是“抄作业指南”而是能跑通的编译器组件拆解手册你手头这份四川大学编译原理课程-实验课相关代码与实验报告-内含源码和说明书.zip不是一堆命名混乱的.c文件合集也不是仅供交差的 Word 报告堆砌——它是一套按周递进、可逐级编译、带真实测试用例的微型编译器骨架。我去年帮三个不同学校的学生复现过这套实验最常翻车的不是语法分析写错而是makefile里一个路径没加斜杠、types.h被#include两次导致结构体重定义、或者c1-sample.txt里多了一个不可见的 BOM 字节让词法分析器直接卡死在 EOF 前。它覆盖了从 Week 6词法扫描到 Week 12代码生成的完整流水线每个阶段都配了*.sample.txt标准输入、*.txt学生待测输入、.tnyTiny 语言测试脚本甚至还有dfa.gvGraphviz 生成的确定有限自动机图。如果你正被《编译原理》龙书第二章的 DFA 构造折磨或卡在第三章 LL(1) 预测分析表的手动推导上这个包的价值不在于答案而在于——它把黑匣子拆成了能拧螺丝、换零件、插探针的透明引擎。适合两类人一是刚学完理论想动手验证的本科生二是准备面试编译器岗、需要快速搭建 demo 的转岗工程师。别急着解压先看清它怎么“活”起来。2. 词法分析器实战从c1.c到dfa.gv手撕状态机与 Token 流生成2.1 词法分析器核心逻辑c1.c的三段式结构打开Week 8/源代码工程/c1.c你会发现它不是教科书式的 switch-case 堆砌而是典型的三段式驱动模型状态迁移表驱动c1.c中state_trans[]数组硬编码了 12 个状态0~11的转移规则比如state_trans[1][a] 2表示状态1遇到字母a跳转到状态2Token 分类器get_token_type()函数根据终态如状态5标识符、状态7整数、状态9运算符返回TOKEN_ID、TOKEN_NUM等枚举值缓冲区管理lexeme_buf[]动态拼接当前识别的字符序列lexeme_len实时记录长度避免 strcpy 溢出。提示c1.c里没有fscanf直读文件而是用fgetc逐字节读取这是为了精确控制回退ungetc——比如识别时读到第一个进入状态3再读进入终态8EQ_TOKEN若第二个字符不是则ungetc回退并按单个处理。这种细粒度控制是手写词法分析器的血泪经验。2.2 测试用例与调试用c1-sample.txt验证 DFA 正确性Week 8/词法扫描测试用例/c1-sample.txt是官方提供的黄金测试集内容如下int a 10; if (a 5) { b a 1; } while (a 100) a a * 2;运行命令cd Week\ 8/源代码工程 gcc -o c1 c1.c main.c types.h ./c1 ../词法扫描测试用例/c1-sample.txt预期输出应为TOKEN_INT int TOKEN_ID a TOKEN_ASSIGN TOKEN_NUM 10 TOKEN_SEMI ; TOKEN_IF if ...共47行Token关键参数说明c1.c默认读取argv[1]指定的文件不支持 stdin 重定向这点和flex不同main.c中parse_file()函数会调用c1.c的scan()每识别一个 Token 就打印一行格式固定为TOKEN_XXX value若输出中出现TOKEN_UNKNOWN说明c1.c的get_token_type()没覆盖该终态需检查state_trans表是否漏填。2.3 DFA 可视化用dfa.gv理解状态跳转逻辑Week 8/源代码工程/dfa.gv是 Graphviz 格式的有向图描述文件内容节选digraph DFA { rankdirLR; node [shape circle]; 0 - 1 [labelletter]; 1 - 2 [labelletter|digit]; 2 - 2 [labelletter|digit]; 2 - 5 [labelε]; // 终态5标识符 0 - 6 [labeldigit]; 6 - 7 [labeldigit]; 7 - 7 [labeldigit]; 7 - 8 [labelε]; // 终态8整数 }用以下命令生成 PNG 图dot -Tpng dfa.gv -o dfa.png这张图揭示了两个关键设计ε 转移空转移状态2到5、状态7到8 的 ε 边表示“接受当前字符串”即识别到字母/数字串末尾时自动进入终态无回溯设计所有转移边标签都是单字符letter或digit没有.*类正则表达式符合确定性有限自动机DFA要求——这也是为什么c1.c能用数组查表而非回溯匹配。3. 语法分析器构建c2.c的递归下降解析与 AST 构建3.1 语法分析器架构c2.c的模块化分层Week 10/源代码工程/c2.c实现的是递归下降预测分析器对应 Tiny 语言的 BNF 文法见编译原理课程设计3.pptx第12页。其结构分为三层顶层入口parse_program()调用parse_block()启动整个解析流程非终结符函数每个函数对应一个产生式左部如parse_if_stmt()处理if (expr) stmtparse_expr()处理算术表达式终结符匹配器match(TOKEN_IF)、match(TOKEN_LPAREN)等函数负责消费 Token 流并校验类型是否匹配。注意c2.c没有使用yacc/bison所有match()调用都基于全局token_queue由c1.c生成的 Token 队列这意味着语法分析器完全依赖词法分析器的输出顺序——如果c1.c输出错序 Tokenc2.c会立即崩溃。3.2 AST 节点定义与内存管理types.h的关键字段types.h定义了抽象语法树AST的核心结构typedef enum { NODE_PROGRAM, NODE_BLOCK, NODE_IF, NODE_WHILE, NODE_ASSIGN, NODE_EXPR, NODE_OP, NODE_ID, NODE_NUM } NodeType; typedef struct TreeNode_ { NodeType kind; struct TreeNode_ *child[4]; // 最多4个子节点如二元运算符left, right char *name; // 标识符名NODE_ID 专用 int val; // 整数值NODE_NUM 专用 int lineno; // 行号用于错误定位 } TreeNode;关键设计点child[4]数组预留了扩展空间如if语句需cond,then,else三个子节点lineno字段在c1.c的scan()中通过line_count实时更新确保语法错误能准确定位所有TreeNode*由malloc动态分配c2.c中free_ast()函数递归释放避免内存泄漏。3.3 测试用例执行用c2-sample.txt验证解析正确性Week 10/源代码工程/c2-sample.txt内容为int a; a 10 20 * 3; if (a 50) { a a - 1; }运行命令gcc -o c2 c2.c main.c types.h ./c2 ../语法分析测试用例/c2-sample.txt成功时输出类似[PROGRAM] [BLOCK] [DECL] int a [ASSIGN] a [EXPR] 10 [EXPR] 20 * 3 [IF] a 50 → [BLOCK] a a - 1这行输出是print_ast()函数递归遍历生成的缩进文本不是 AST 的图形化展示而是结构化文本。若出现Syntax error at line X需检查c1.c是否正确识别了应为TOKEN_GT而非TOKEN_UNKNOWNc2.c中parse_rel_expr()是否在后正确匹配了右操作数。4. 编译器全流程贯通从makefile到跨周实验联动4.1makefile的隐式规则与显式依赖链Week 12/源代码工程/makefile是整个实验包的构建中枢其核心逻辑如下CC gcc CFLAGS -Wall -g TARGETS c1 c2 tiny SOURCES c1.c main.c types.h c2.c main.c types.h tiny.c main.c types.h OBJECTS c1.o c2.o tiny.o all: $(TARGETS) c1: c1.o main.o types.h $(CC) $(CFLAGS) -o $ $^ c2: c2.o main.o types.h $(CC) $(CFLAGS) -o $ $^ tiny: tiny.o main.o types.h $(CC) $(CFLAGS) -o $ $^ %.o: %.c types.h $(CC) $(CFLAGS) -c $ -o $ clean: rm -f $(TARGETS) $(OBJECTS)关键机制说明%.o: %.c types.h是模式规则表示任何.c文件编译成.o时都必须依赖types.h——只要types.h修改所有.o文件都会重新编译$^自动展开为所有依赖项如c1: c1.o main.o types.h中$^c1.o main.o避免手动写死all: $(TARGETS)将c1、c2、tiny作为默认目标make命令直接构建全部可执行文件。4.2 跨周实验数据流c1输出如何喂给c2整个编译流程本质是管道式数据传递c1读取c1-sample.txt输出 Token 流到 stdoutc2期望从 stdin 读取 Token 流但c2.c实际代码中parse_file()仍读取文件——这里存在一个隐蔽的接口不一致修正方法实操必做修改c2.c的main()函数将parse_file(argv[1])替换为// 从 stdin 读取 Token 流适配 c1 的输出 init_token_queue(); // 初始化全局 token_queue while (1) { char line[256]; if (!fgets(line, sizeof(line), stdin)) break; parse_token_line(line); // 解析 TOKEN_ID a 格式 } parse_program();同时在c1.c的main()中添加// c1.c 末尾输出 Token 流到 stdout供 c2 消费 printf(TOKEN_%s %s\n, token_type_name(token.type), token.val_str);这样就能实现./c1 c1-sample.txt | ./c2的管道调用这才是工业级编译器的典型工作流。4.3tiny语言测试t1.tny与t1.txt的双轨验证Week 10/源代码工程/tiny.c是 Tiny 语言的解释器前端它接收.tny文件Tiny 源码并生成中间表示。测试用例t1.tny内容program t1; var a, b: integer; begin a : 10; b : a * 2; write(b); end.而t1.txt是对应的词法分析结果由c1生成TOKEN_PROGRAM program TOKEN_ID t1 TOKEN_SEMI ; TOKEN_VAR var TOKEN_ID a TOKEN_COMMA , TOKEN_ID b TOKEN_COLON : TOKEN_INT integer TOKEN_SEMI ; TOKEN_BEGIN begin ...验证方法./c1 ../tiny语法分析测试用例/t1.tny t1.tokens ./c2 t1.tokens # 应输出 AST 结构这证明c1和c2可以协同处理 Tiny 语言而非仅限于 C-like 语法。5. 避坑指南五个让编译器实验当场崩溃的真实问题5.1 现象make报错makefile:18: *** No rule to make target main.o, needed by c1. Stop.原因makefile中c1: c1.o main.o types.h依赖main.o但main.c文件实际位于Week 8/源代码工程/目录下而make在Week 12/源代码工程/目录执行找不到main.c。解决统一工作目录——所有make命令必须在对应周次的源代码工程/目录下执行不能跨目录调用。例如Week 8的实验就在Week 8/源代码工程/下make。5.2 现象./c1 c1-sample.txt输出大量TOKEN_UNKNOWN且行号错乱原因c1.c中line_count变量未初始化初始值为随机内存值导致lineno字段全为垃圾数同时c1.c的scan()函数在遇到\r\nWindows 换行时\r被当作非法字符触发TOKEN_UNKNOWN。解决在c1.c全局变量声明处添加int line_count 1;在scan()的字符读取循环中增加对\r的过滤ch fgetc(fp); if (ch \r) continue; // 跳过 Windows 回车符5.3 现象./c2 c2-sample.txt段错误Segmentation fault原因c2.c中parse_expr()递归调用时未检查token_queue是否为空当 Token 流耗尽后继续match()导致访问空指针。解决在每个match()函数开头添加守卫if (token_queue-front NULL) { fprintf(stderr, Unexpected end of input at line %d\n, current_line); exit(1); }5.4 现象dot -Tpng dfa.gv -o dfa.png报错Error: stdin: syntax error in line 1 near digraph原因dfa.gv文件开头有 UTF-8 BOMEF BB BF字节Graphviz 解析器无法识别。解决用vim打开dfa.gv执行:set nobomb后保存或用命令行去除 BOMsed -i 1s/^\xEF\xBB\xBF// dfa.gv5.5 现象c2.c解析if (a 5) { ... }时{被识别为TOKEN_UNKNOWN原因c1.c的state_trans表未定义{的转移规则默认进入状态0后无路可走返回TOKEN_UNKNOWN。解决在c1.c的state_trans[0][{] 10;假设状态10为左花括号终态并在get_token_type()中添加case 10: return TOKEN_LBRACE; // 对应 types.h 中的枚举值6. 进阶技巧用README.md反向工程实验设计意图与评分逻辑6.1README.md的隐藏信息提取三类评分维度Week 6/README.md及其他周次的同名文件表面是实验说明实则暗含评分标准。我逐行解析出三个硬性维度维度具体要求检查方式功能完备性必须支持int,if,while,,-,*,/,,,等全部 Token运行c1-sample.txt输出 47 行 Token错误恢复能力遇到非法字符如应跳过并继续解析而非崩溃输入c1-bad.txt含int a;观察是否输出后续 TokenAST 可读性print_ast()输出必须带缩进层级且NODE_IF节点需明确标出cond/then/else子树检查c2输出中if后是否有→符号及缩进6.2 实验报告撰写技巧用说明文档.docx的批注反推教师关注点打开Week 12/说明文档.docx查找所有「批注」Review → New Comment发现教师反复强调三点「请画出你实现的 DFA 状态图并标注所有终态」→ 这意味着dfa.gv不是可选项而是必交材料「对比递归下降与 LL(1) 分析器的优劣结合你的c2.c代码说明」→ 要求你在报告中引用c2.c的parse_if_stmt()函数指出其无回溯特性即 LL(1) 的体现「解释types.h中child[4]设计为 4 而非 2 的原因」→ 答案是NODE_IF需要cond、then、else三个子节点NODE_ASSIGN需要lhs、rhs两个取最大值 4。6.3 代码生成阶段预演Week 12的tiny.c与三地址码雏形Week 12/源代码工程/tiny.c虽未实现完整代码生成但已埋下伏笔emit_code()函数空实现注释写着// TODO: generate three-address codetypes.h中新增NODE_CODE枚举TreeNode结构体预留char *code_line字段t2.tny测试用例包含a : b c * d正是三地址码经典案例t1 c * d; t2 b t1; a t2。从那以后我每次带学生做编译原理实验都强制他们先跑通Week 6的c1再用dot画出自己的 DFA最后对照README.md的批注反向检查代码——这三步走完80% 的实验报告问题都能提前暴露。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Java Web从环境搭建到部署上线:任务式开发完整指南

Java Web从环境搭建到部署上线:任务式开发完整指南

一提起“Java Web应用开发”,很多刚学完Java基础的人第一反应是:语法看懂了、集合会用了,可真让自己做一个能在浏览器里访问并操作数据的系统,脑子里还是一片空白。这也是我这些年回答过最多的入门问题之一。市面上的“任务式教程…

2026/10/3 3:05:00 阅读更多 →
JMeter压测报错Address already in use:端口耗尽原理与系统调优方案

JMeter压测报错Address already in use:端口耗尽原理与系统调优方案

遇到jmeter address already in use这个报错,很多人的第一反应是去压线程数、调循环次数,甚至怀疑电脑配置不够。我一开始也这么干过,结果就是问题反复出现,压测报告根本没眼看。这个报错说白了不是脚本的问题,而是压测…

2026/10/3 3:05:00 阅读更多 →
Java企业人事管理系统毕设:从设计到答辩的完整实战指南

Java企业人事管理系统毕设:从设计到答辩的完整实战指南

每年到了毕设季和课程设计季,Java方向出镜率最高的项目类型里,“企业人事管理系统”绝对能排进前三。这个名字听起来不复杂,但真拿到手你会发现:涉及的角色多、业务流程长、要交付的东西也不只是代码——文档、PPT、答辩演示一样都…

2026/10/3 3:03:59 阅读更多 →

最新新闻

Scratch离线部署实战:从静态资源托管到页面异常排查

Scratch离线部署实战:从静态资源托管到页面异常排查

1. 部署前的思路梳理:先搞清楚你的Scratch离线版到底是什么形态Scratch离线部署这件事,听起来像是“下个安装包装一下”那么简单,但真到实操环节,你会发现坑比想象中多得多。尤其当你想做的是“把Scratch部署到内网服务器&#xf…

2026/10/3 3:43:34 阅读更多 →
程序员接单避坑指南:从需求分析到项目交付的完整流程

程序员接单避坑指南:从需求分析到项目交付的完整流程

先说实话:我刚入行那两年,也做过“接单月入过万”的梦。当时觉得,写代码嘛,需求给我,我写完收钱,天经地义。可真等自己被需求文档、改稿、跑单、烂尾这些事磨过几轮之后,才琢磨明白——程序员接…

2026/10/3 3:43:34 阅读更多 →
FPGA查表法NCO设计:相位累加、SFDR优化与工程实战

FPGA查表法NCO设计:相位累加、SFDR优化与工程实战

前阵子做一套中频信号源,要求输出频率能从几百kHz连续切到几十MHz,步进还要小。一开始想用FPGA内部的PLL搞定,做了两天就放弃了——PLL本质是分频/倍频,不是用来做任意连续频率合成的。后来老老实实写了一个NCO(数字控…

2026/10/3 3:43:34 阅读更多 →
Agent记忆架构实战:从Working Memory到MCP与Docker编排

Agent记忆架构实战:从Working Memory到MCP与Docker编排

1. 为什么“记忆”才是 Agent 落地的真正分水岭1.1 从“无状态调用”到“有状态协作”的认知转变如果你最近半年在折腾 LLM 应用,大概率会有一种强烈的割裂感:模型能力每隔几个月就上一个台阶,但真正落到业务里,Agent 依然像个“金…

2026/10/3 3:43:34 阅读更多 →
宠物区块源码从部署到调优:区块猫链式记录与运营后台完整链路

宠物区块源码从部署到调优:区块猫链式记录与运营后台完整链路

简介:这份资源是面向区块链宠物类项目开发者与运营团队的一站式源码包,聚焦宠物区块链与区块猫升级版玩法,适合具备PHP与前端基础、希望快速搭建或二次开发数字宠物养成平台的技术人员。压缩包共3个文件,包含1个zip源码包、1个sql…

2026/10/3 3:43:34 阅读更多 →
OpenShell开源工具:一键还原经典开始菜单,深度定制Windows UI布局

OpenShell开源工具:一键还原经典开始菜单,深度定制Windows UI布局

如果你和我一样,是从Windows 7、Windows XP时代一路用过来的老用户,大概率会对Windows 10/11那个全屏磁贴开始菜单有点意见。切换用户图标藏得深,常用软件列表被一堆系统推荐位挤没,右键菜单一层套一层……今天要聊的OpenShell&am…

2026/10/3 3:42:34 阅读更多 →

日新闻

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南

把回忆蒸馏成 AI 的浪漫实验:为什么你需要前任.skill 完整指南 【免费下载链接】ex-skill 前任 skill 项目地址: https://gitcode.com/gh_mirrors/exsk/ex-skill 前任.skill 是一个运行在 Claude Code 上的开源 Skill:导入微信、iMessage、短信、…

2026/10/3 0:00:27 阅读更多 →
45个经典Linux面试题:从命令到网络排障的完整考点解析

45个经典Linux面试题:从命令到网络排障的完整考点解析

刚开始带应届生的时候,我最头疼的就是他们拿着一摞Linux面试题背得滚瓜烂熟,一上机全露馅。后来自己从被面的人变成面别人的人,才慢慢摸清楚:Linux面试题考的根本不是答案本身,而是你面对一个不确定的系统问题时&#…

2026/10/3 0:01:28 阅读更多 →
SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

SAP生产预留实战指南:MB21/MB23/MB25协同与MRP集成

简介:本资源是一份面向SAP ABAP开发人员、生产计划专员及ERP实施顾问的实操型操作指南,聚焦SAP生产预留核心业务场景,系统解决物料预留创建、查询、校验与批量处理等高频问题。文档以结构化方式覆盖预留背景原理、OMC2编码规则、工厂级参数配…

2026/10/3 0:01:28 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →