简介:本资源是一套面向编程语言学习者与编译原理初学者的《自制编程语言》系统性入门资料,聚焦语言设计、实现与工程实践全过程。内容覆盖语法语义设计原则、基于bison/flex的词法/语法分析器开发、MinGW环境下的编译器构建、Crowbar与Diksam两款真实自制语言的完整演进版本(v0.1–v0.4)及配套源码、Linux/Windows双平台构建脚本(make.sh/make.bat)与运行指南,兼具理论框架与可编译实操案例。资源为单个PDF文件,大小2.38MB,结构清晰,含多版本书籍文档、环境配置详解、GCC/MinGW安装步骤及Cygwin对比说明,便于按需查阅与本地复现。目前已有870人学习下载,适合高校计算机专业学生、自学者及对编译器开发感兴趣的开发者,快速掌握从语言定义到可执行解释器落地的关键技术路径。
1. 装 Crowbar 和 Diksam 前,先搞清你到底在编译什么:不是“跑个 hello world”,而是亲手搭一座语法解析器的桥
你下载了《自制编程语言》资料包,解压后看到crowbar_book_0_1到0_4、diksam_book_0_1到0_4,还有llparser、mycalc、onig-5.9.4……第一反应可能是:“这不就是几本电子书+几个 C 源码目录?照着make一下不就完了?”——然后卡在bison: command not found,或y.tab.h: No such file or directory,或更玄学的m4: cannot open 'Files': No such file or directory。这不是你手残,是这套资料从诞生起就踩在「跨平台工具链撕裂」的刀尖上:它用的是 2009–2013 年间最典型的国产 Windows 开发者环境组合——MinGW + GNUWin32 的 bison/flex + 手动编译 onig 正则库,而所有 Makefile 都默认你已把路径、编码、依赖顺序全调对。我见过太多人花三天重装 Cygwin、删光Program Files (x86)下所有带空格的路径、反复dos2unix文件,最后发现错在crowbar.l里一行没注释掉的%start COMMENT STRING_LITERAL_STATE。这不是编程语言入门,这是逆向工程一套十年前的构建黑匣子。适合谁?适合想真正理解「lexer → parser → AST → interpreter」链条如何落地的实践派;不适合只想抄段代码跑 demo 的速成党。它不教你怎么设计语义,但逼你亲手把 yacc/lex 的每行规则、每个#include "y.tab.h"的生成时机、每个ranlib libonig.a的链接逻辑,都抠到字节级。装不好?轻则make报错停在第 3 行,重则生成一个能启动却在读\r\n时直接 segfault 的crowbar.exe——因为源码里st_line是未初始化指针,而 Windows 控制台默认 CRLF 换行。下面,我们就按当年作者实际走通的路径,一砖一瓦重建这套环境。
2. 环境筑基:MinGW、bison、flex、onig —— 四件套的版本锁与路径洁癖
这套资料的生命线,系于四个底层工具的协同:MinGW 提供 GCC 编译器和make,bison/flex 生成词法与语法分析器,onig 提供正则引擎(Crowbar 0.3+ 开始引入)。它们不是独立存在,而是彼此咬合的齿轮——bison 依赖 m4 宏处理器,flex 依赖 bison 生成的头文件,onig 编译后必须被libonig.a静态链接进 Crowbar,而所有路径都不能含空格、不能用中文、不能跨盘符。任何一环松动,整个构建链就崩。
2.1 MinGW:选对安装器,避开mingw-get-setup.exe的历史坑
官方 MinGW 已停止维护,当前最稳定兼容 Crowbar/Diksam 的是MinGW 5.1.6(对应 GCC 4.5.2),而非新版 MinGW-w64。原因很实在:Crowbar 的Makefile中硬编码了mingw32-make.exe,且其configure脚本检测alloca()的方式与新版 MinGW 不兼容。
提示:不要用
mingw-get-setup.exe(旧版图形安装器),它会默认安装到C:\MinGW但跳过关键组件。必须用命令行方式手动安装基础包。
# 下载地址(SourceForge 归档镜像) # https://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe/download # 运行后选择 "Continue" → "Installation Directory" 改为 D:\MinGW(强烈建议非系统盘、无空格、英文路径) # 在 "MinGW Installation Manager" 中,勾选以下三项并 Apply: # - mingw32-base (核心 GCC 工具链) # - mingw32-gcc-g++ (C/C++ 编译器) # - msys-base (提供基本 shell 环境,含 make)安装完成后,必须将D:\MinGW\bin和D:\MinGW\msys\1.0\bin同时加入系统 PATH(注意顺序:msys\1.0\bin必须在bin之前,否则make会调用错版本)。验证:
# cmd 中执行 where make # 应返回 D:\MinGW\msys\1.0\bin\make.exe where gcc # 应返回 D:\MinGW\bin\gcc.exe若where make返回C:\Windows\System32\make.exe(Windows 自带假货),说明 PATH 顺序错误,需调整。
2.2 bison & flex:GNUWin32 的 2.4.1 + 2.5.4 组合是唯一安全解
Crowbar 0.1–0.4 全系列 Makefile 基于bison 2.4.1和flex 2.5.4编写。新版 bison(3.x)默认启用--update模式,会覆盖旧版y.tab.c生成逻辑;新版 flex(2.6+)的yywrap()行为变更,导致crowbar.l中自定义的 EOF 处理失效。必须降级。
# 下载 GNUWin32 旧版包(SourceForge 归档) # bison 2.4.1: https://sourceforge.net/projects/gnuwin32/files/bison/2.4.1/ # flex 2.5.4: https://sourceforge.net/projects/gnuwin32/files/flex/2.5.4/ # 解压后,将 bin 目录下所有 .exe(bison.exe, flex.exe, m4.exe)复制到 D:\MinGW\bin\ # 特别注意:m4.exe 是 bison 的依赖,缺它必报 "m4: cannot open 'Files'" 错误关键避坑:
m4: cannot open 'Files'错误的根源是 bison 调用 m4 时传入了含空格的路径(如C:\Program Files\...),而 GNUWin32 的 m4 不支持引号包裹路径。解决方案只有两个:① 把 bison 安装到无空格路径(如D:\tools\bison)并加入 PATH;②更彻底的做法:直接用 MinGW 自带的 m4(D:\MinGW\msys\1.0\bin\m4.exe),它原生支持空格路径。我们选后者,删除 GNUWin32 的m4.exe,确保where m4返回的是 MSYS 版本。
2.3 oniguruma:5.9.4 静态库的手工编译与ranlib救命术
Crowbar 0.3 引入正则支持,依赖 oniguruma。官方推荐onig-5.9.4.tar.gz(2013 年发布),新版(6.x)因 API 变更无法编译通过。它必须以静态库.a形式链接,因为 Crowbar 的 Makefile 中写死-lonig,且 Windows 下无libonig.dll分发机制。
# 下载地址(作者指定) # http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.4.tar.gz # 解压到 C:\onig-5.9.4(路径必须全英文、无空格) cd C:\onig-5.9.4 # 运行 MSYS shell(D:\MinGW\msys\1.0\msys.bat),在其中执行: ./configure --prefix=D:/MinGW --enable-static --disable-shared make # 此时不会自动运行 make install,因为默认安装路径是 /usr/local,而 MinGW 不认 # 我们手动提取 libonig.a: cp .libs/libonig.a D:/MinGW/lib/ # 关键一步:MinGW 的 ar 工具需要 ranlib 生成索引 D:/MinGW/bin/ranlib.exe D:/MinGW/lib/libonig.a # 同时复制头文件 mkdir -p D:/MinGW/include/oniguruma cp oniguruma.h D:/MinGW/include/oniguruma/ cp regenc.h D:/MinGW/include/oniguruma/注意:
make install在 MinGW 下会失败(报No such file or directory),因为libtool尝试写入/usr/local/lib。手工复制 +ranlib是唯一可靠路径。ranlib不是可有可无的装饰,没有它,gcc -lonig会报cannot find -lonig,因为链接器找不到符号索引。
3. Crowbar 构建实战:从 0.1 到 0.4 的 Makefile 适配与源码补丁
Crowbar 的四个版本(0.1–0.4)不是简单升级,而是构建逻辑的渐进式重构。0.1 用纯make,0.2 引入configure,0.3 加入 onig,0.4 重构内存管理。直接make0.4 会失败,必须按版本分步处理,并打上社区验证过的补丁。
3.1 Crowbar 0.1:最简路径,验证 lexer/parser 流水线
crowbar_book_0_1是起点,结构最干净:crowbar.y(Bison 语法)、crowbar.l(Flex 词法)、main.c(入口)。它的 Makefile 是手写的,不依赖configure。
# 解压到 D:\crowbar_book_0_1(路径必须英文、无空格、不在系统盘根目录) cd D:\crowbar_book_0_1 # 在 MSYS shell 中执行(非 cmd!MSYS 提供 bash 环境) # 先生成 parser 和 lexer: bison --yacc -dv crowbar.y # 生成 y.tab.c 和 y.tab.h flex crowbar.l # 生成 lex.yy.c # 再编译(注意:必须用 mingw32-gcc,且显式链接 math 库) gcc -o crowbar.exe main.c y.tab.c lex.yy.c -lm # 若报错 "undefined reference to `pow`",说明 -lm 位置错误,应放在最后为什么不用
make?因为crowbar_book_0_1/Makefile中CC = gcc未指向 MinGW 版本,且未加-lm。手动编译是最可控的验证方式。成功后运行./crowbar.exe test/test.crb,应输出Hello, World!。
3.2 Crowbar 0.2:configure脚本的 Windows 兼容性补丁
crowbar_book_0_2引入configure脚本,但原始脚本为 Unix 设计,在 MSYS 下会因换行符(CRLF)和test命令行为差异失败。必须预处理:
# 在 MSYS shell 中执行(确保 dos2unix 已安装,若无则 `apt-cyg install dos2unix`) cd D:\crowbar_book_0_2 dos2unix configure chmod +x configure # 运行 configure,强制指定 MinGW 工具链 ./configure --host=mingw32 --build=mingw32 CC=gcc CFLAGS="-g -Wall" # 此时会生成 Makefile,但仍有两处需手动修改: # 1. 打开 Makefile,找到 LDFLAGS 行,末尾添加 -lm # 2. 找到 LIBS 行,改为 LIBS = -lm make3.3 Crowbar 0.3/0.4:onig 链接与st_line空指针崩溃修复
0.3+ 版本在interface.c中引入onig_search(),且lexicalanalyzer.c存在经典空指针 bug(static char *st_line;未 malloc)。不修复则crowbar.exe启动即崩溃。
# 先应用 st_line 补丁(来自 2014 年社区反馈) # 编辑 D:\crowbar_book_0_3\src\lexicalanalyzer.c # 将原代码: # static char *st_line; # while (fgets(st_line, LINE_BUF_SIZE, stdin) != NULL) { ... } # 替换为: # char st_line[LINE_BUF_SIZE]; // 栈上分配,避免 malloc/free # while (fgets(st_line, LINE_BUF_SIZE, stdin) != NULL) { ... } # 然后编译: cd D:\crowbar_book_0_3 ./configure --host=mingw32 CC=gcc CFLAGS="-g -Wall -DONIG_ESCAPE_UCHAR_COLLISION" make # 关键参数 -DONIG_ESCAPE_UCHAR_COLLISION:解决 onig 5.9.4 与 MinGW wchar_t 类型冲突常见问题排查:若
make报undefined reference to 'onig_search',检查libonig.a是否在D:\MinGW\lib\,且ranlib是否执行成功;若报error: 'ONIG_ENCODING_UTF8' undeclared,说明oniguruma.h头文件未正确复制到D:\MinGW\include\oniguruma\。
4. Diksam 构建与跨平台陷阱:为什么 Linux 下crowbar跑得通,diksam却报libmsvcp60.so?
Diksam 是 Crowbar 的面向对象演进版,但它的构建更脆弱——因为它在 Linux 下尝试链接 Windows 的 VC++ 运行时库(libmsvcp60.so),这是典型的交叉编译污染。根源在于diksam_book_0_4的configure脚本错误检测了宿主系统的 C++ 库,而非目标平台。
4.1 Windows 下 Diksam 0.4 的纯净构建
Diksam 0.4 的 Makefile 显式调用g++,因此必须确保 MinGW 的 g++ 可用,且禁用所有 Windows 特定链接选项:
cd D:\diksam_book_0_4 # 清理旧配置 rm -f config.status config.log Makefile # 重新 configure,严格限定工具链 ./configure --host=mingw32 --build=mingw32 \ CC=gcc CXX=g++ \ CFLAGS="-g -Wall -D_WIN32" \ CXXFLAGS="-g -Wall -D_WIN32" \ LDFLAGS="-static-libgcc -static-libstdc++" # 修改 Makefile:搜索 "-lmsvcp60" 并删除整行 make
-static-libgcc -static-libstdc++是关键:它强制将 C++ 运行时静态链接,避免运行时依赖libmsvcp60.dll。否则生成的diksam.exe在无 VC++ Redist 的机器上直接报错。
4.2 Linux 下构建 Crowbar/Diksam 的反直觉操作
Linux 用户常以为./configure && make就完事,但crowbar_book_0_1的Makefile默认使用gcc -o crowbar,而 Linux 下需链接readline库(用于交互式输入)。不加则crowbar启动后无法输入:
# Ubuntu/Debian 下先安装依赖 sudo apt-get install bison flex libreadline-dev cd ~/crowbar_book_0_1 # 手动编译,显式链接 readline gcc -o crowbar main.c y.tab.c lex.yy.c -lreadline -lm # 或修改 Makefile,将 LDLIBS = -lreadline -lm4.3 常见问题与血泪排查:5 条真实翻车记录
以下是我在复现过程中踩过的坑,每一条都对应真实报错日志:
现象:
make报错process_begin: CreateProcess(NULL, bison --yacc -dv crowbar.y, ...) failed.
原因:bison.exe不在 PATH,或bison.exe依赖的msvcrt.dll版本不匹配(GNUWin32 二进制依赖旧版 CRT)。
解决:用Dependency Walker检查bison.exe依赖,替换为 MinGW 自带的bison(从D:\MinGW\msys\1.0\bin\复制),或重装 GNUWin32 到无空格路径。现象:
mycalc.l:3:19: error: y.tab.h: No such file or directory
原因:bison未成功生成y.tab.h,或flex扫描路径时未包含当前目录(#include "y.tab.h"要求y.tab.h与.l文件同目录)。
解决:先手动运行bison -dv crowbar.y,确认生成y.tab.c和y.tab.h;再检查flex命令是否在crowbar.l所在目录执行。现象:
/usr/bin/ld: cannot find -lmsvcp60(Linux 下编译 Diksam)
原因:configure脚本错误检测到系统有libmsvcp60.so(可能来自 Wine 或旧软件),并写入 Makefile。
解决:编辑config.log,搜索msvcp60,找到检测逻辑;在configure前设置ac_cv_lib_msvcp60_no=no环境变量,或直接修改Makefile删除-lmsvcp60。现象:
crowbar.exe启动后输入任意代码,立即弹窗报错0xC0000005: Access Violation
原因:lexicalanalyzer.c中st_line未初始化,fgets向随机地址写入。
解决:按 3.3 节方案,将static char *st_line;改为char st_line[LINE_BUF_SIZE];,这是 Crowbar 0.1–0.4 全系列通病。现象:
make install报错Makefile:415: recipe for target 'install-libLTLIBRARIES' failed,提示No such file or directory
原因:libtool尝试写入/usr/local/lib,但 MinGW 的 MSYS 根目录映射为D:\MinGW\msys\1.0\,/usr/local实际不存在。
解决:放弃make install,改用手工复制:cp .libs/libonig.a D:/MinGW/lib/ && D:/MinGW/bin/ranlib.exe D:/MinGW/lib/libonig.a。
5. 验证与调试:用三组测试用例确认你的 Crowbar/Diksam 真正跑通
装完不是终点,验证才是。我设计了三组递进式测试,覆盖 lexer、parser、runtime 三层,每组都附带预期输出和失败诊断路径。它们不是玩具代码,而是从 Crowbar 原始 test suite 中提炼出的“压力探针”。
5.1 Lexer 层验证:test_lex.crb—— 检查词法分析器是否吞掉换行符
创建test_lex.crb:
print "line1"; print "line2"; # 这里故意留空行 print "line3";预期输出:
line1line2line3失败诊断:
- 若输出
line1line2(缺失line3),说明st_line空指针补丁未生效,fgets在空行后返回NULL但未跳出循环; - 若输出
line1line2line3但多出乱码(如 ``),说明源文件编码为 UTF-8 with BOM,而 Crowbar 0.1–0.2 默认按 SJIS 解析。用 Notepad++ 转为UTF-8 without BOM。
5.2 Parser 层验证:test_parser.crb—— 触发 Bison 语法错误捕获
# 语法错误:缺少右括号 if (1 == 1 { print "true"; }预期输出:
crowbar: test_parser.crb:2: syntax error near '{'失败诊断:
- 若程序直接崩溃(segfault),说明
crowbar.y的错误恢复规则未触发,检查crowbar.y中%error-verbose是否开启,或yyerror()函数是否被正确链接; - 若静默退出无报错,说明
bison未生成错误处理代码,确认bison --yacc -dv crowbar.y命令中-dv参数存在(生成详细调试信息)。
5.3 Runtime 层验证:test_gc.crb—— 压力测试垃圾回收器(Crowbar 0.3+)
# 创建 10000 个字符串,触发 GC var i = 0; while (i < 10000) { var s = "hello_" + i; i = i + 1; } print "done";预期输出:
done(且内存占用稳定,无 crash)
失败诊断:
- 若报
Out of memory,说明onig正则引擎未链接,crowbar的字符串池分配失败; - 若
done输出后程序卡死,说明crowbar的 mark-sweep GC 在遍历onig对象时崩溃,检查onig-5.9.4是否用--enable-static编译,且crowbar链接时未遗漏-lonig。
从那以后我每次构建 Crowbar/Diksam,都强制走一遍这三组测试:先
test_lex.crb确认 lexer 不吃换行,再test_parser.crb确认 parser 能吐错误,最后test_gc.crb确认 runtime 不崩。少一个环节,就等于埋了一颗不知道何时引爆的雷。这套资料的价值,不在于它多先进,而在于它强迫你把编译器的每一层都亲手拧紧——lexer 的缓冲区大小、parser 的错误恢复点、GC 的对象标记位,全是肉眼可见、可调试、可修改的 C 代码。它不给你云里雾里的抽象,只给你一把螺丝刀和一张电路图。希望帮到你。
本文还有配套的精品资源,点击获取