简介:《自制编程语言》相关资料1是一份面向编程语言爱好者与编译器入门者的PDF文档,聚焦从语言设计、实现到实际使用的完整链路,适合希望理解解释器与编译器工作原理、并尝试动手实现小型语言的读者。整包仅含1个PDF文件,约2.38MB,内容集中,便于系统通读与按主题检索,目前已有870人学习。文档围绕语法与语义设计、可读性与效率权衡展开,介绍了借助yacc/lex、bison/flex生成词法分析与语法分析器的思路,并说明MinGW在Windows下搭建GCC编译环境的方式。文中还以Crowbar和Diksam两个自制语言为示例,串联编译器与解释器的实现、运行时资源管理、垃圾回收版本演进,以及Linux与Windows下构建脚本和工具链差异,可帮助读者建立从语言设计到可执行程序的整体认知。
1. 先认清这套源码包:Crowbar 与 Diksam、八个目录和两套构建脚本
很多人以为自制编程语言就是写个解释器跑两行算数,真把源码拖下来才发现,光让 make 走完一遍就能耗掉一下午。这套 ZIP 里躺着两门完整的自制编程语言实现:Crowbar 0.1 到 0.4、Diksam 0.1 到 0.4,外加 calc、mycalc、mycalc_ex 这类 yacc/lex 小例子,以及 llparser、llparser_ex 两个手写递归下降的对照版本。每个目录都配了 Linux 的make.sh和 Windows 的make.bat,看着贴心,踩下去才知道 Windows 要的是 gmake 不是 make,缺 bison 就直接卡在 y.tab.h,Diksam 0.3 之后还多一个 oniguruma 正则库。下面按两条线走一遍,把版本、路径、参数和翻车后的救法写清楚,适合刚啃编译原理、想要一套能编过能跑起来的源码练手的人。
2. 搭工具链:MinGW 加 bison、flex 三个包的选型与落位
Windows 上编译这套源码,本质是在一个假的类 UNIX 环境里跑标准的 C 构建流程。三样东西缺一不可:一个能出gcc的 MinGW、一个能生成语法分析器的 bison、一个能生成词法分析器的 flex。三者都是绿色安装包,装错地方的代价却完全不对等——MinGW 装歪了顶多重装,bison 装歪了会在编译中途给你一串莫名其妙的 m4 报错,查半天才发现是路径里有个空格。
2.1 MinGW 只勾 mingw32-base,别整包往下拖
MinGW 的安装器叫mingw-get-setup.exe,运行之后第一步是选安装目录(Installation Directory)。这一步是全文第一个关键决策点:千万不要接受默认的C:\Program Files\MinGW,改成D:\MinGW这种纯字母、无空格、无括号的短路径。
装完引导器会拉起 MinGW Installation Manager,左侧是包分类,右侧是包列表。常见做法是只勾 Basic Setup 分类下的mingw32-base,它把 gcc、binutils、mingw32-make 一起带进来,刚好覆盖这套源码需要的全部内容。g++ 不用勾,源码是纯 C;msys-base 可选,装了能多一个 shell,但不装也不影响。勾完点左上角 Installation → Apply Changes,等进度条走完。
装完先验证一次:
# 在 cmd 或 PowerShell 里执行,确认 gcc 真的落地了 D:\MinGW\bin\gcc.exe -v # 顺手看一下 make 有没有一起进来 dir D:\MinGW\bin\mingw32-make.exe第一条命令会打印出 gcc 的版本和 configure 参数,看到gcc version字样就说明基础套件齐了。第二条确认mingw32-make.exe存在——它是 GNU make 在 MinGW 下的改名版,后面 Crowbar 和 Diksam 的构建全靠它。如果这一步就找不到文件,多半是 Apply Changes 只更新了列表没真正下载,回 Installation Manager 里重新点一次。
2.2 bison 与 flex 选 Complete package,路径里不能有括号
bison 和 flex 在 Windows 上没有官方二进制,这套资料里推荐的是 GnuWin32 打包的版本。下载页上一般会列好几行,选Complete package, except sources那一行的 exe,双击一路 next 就行。这里埋着一个流传很广的坑:默认安装路径是C:\Program Files (x86)\GnuWin32,带空格又带括号,bison 运行时会把这个路径原样拼进它调用 m4 的命令行里,空格一断,m4 就报cannot open 'Files'这种看着完全不知所云的错。
我的习惯是手动把路径改成D:\GnuWin32\bison和D:\GnuWin32\flex,两个包装在同一层父目录下,互不干扰又方便统一加 PATH。装的时候如果安装器问要不要装源码,一律不要,这套源码要的是可执行文件不是 bison 自己的源码。
flex 同理,装到D:\GnuWin32\flex。两个包都装完之后各自bin目录下应该有bison.exe、m4.exe(bison 依赖它)和flex.exe,缺 m4 的话 bison 会在运行时报找不到宏处理器。
2.3 三条 PATH 的顺序,决定你用的是哪个 gcc
三个 bin 目录都要进系统 PATH:D:\MinGW\bin、D:\GnuWin32\bison\bin、D:\GnuWin32\flex\bin。加完之后新开一个 cmd 窗口(旧窗口读不到新环境变量,这一点每年都有人栽),跑三条验证:
# 三条都要求能打印版本,任何一条报"不是内部或外部命令"就是 PATH 没生效 gcc -v bison --version flex --version # 看命中的到底是哪个 gcc,机器上装过 IDE 的尤其要查 where gccwhere gcc这条最值得养成习惯。不少机器上装过带 gcc 的 IDE 或者别的工具链,PATH 顺序一乱,编译时用的是另一个版本的 gcc,报错信息和你查到的资料完全对不上。原则很简单:D:\MinGW\bin放在前面,让这套源码专用。三个命令都能出版本号,工具链这一层就算过了。
提示:加完 PATH 之后如果 cmd 里还是找不到命令,先确认加的是系统变量而不是某个用户变量,再确认没有把
%PATH%写掉导致覆盖。
3. 编译 Crowbar:从 crowbar_book_0_1 到 0_4 的 make 流程
Crowbar 是这套资料里最适合入门的部分,代码量小、依赖干净,只要 gcc 和 bison/flex 到位就能出可执行文件。四个版本目录一一对应书里的四章,crowbar_book_0_1是最朴素的解释器,往后每加一章就多一层能力,到crowbar_book_0_4已经是带完整语法树的版本。先啃通 0_1,后面的版本基本是同一套构建流程。
3.1 四个版本目录的差异与构建入口
打开任意一个crowbar_book_0_x目录,会看到一堆.c、.h,加上crowbar.y、crowbar.l和Makefile。crowbar.y是 bison 的语法描述,crowbar.l是 flex 的词法描述,两者编译之后才会产出中间文件,所以第一次构建的顺序是固定死的:先 bison 出y.tab.c和y.tab.h,再 flex 出lex.yy.c,最后 gcc 把所有.c一起链成crowbar。
这个顺序写在 Makefile 里,但 Makefile 的写法在四个版本之间有细微差别。0_1 的规则相对简单,0_2 之后把依赖拆得更细,出错的时候报错行号会不一样(我见过Makefile:29和Makefile:30两种)。这不影响构建逻辑,但如果搜到别人的报错截图对不上行号,别慌,版本不同而已。
make.sh和make.bat分别对应两个平台。Linux 上make.sh里基本就是一串make,直接执行或者干脆跳过它自己敲make都行。Windows 上make.bat实际调的是 GNU make,问题在于这条链路上做了一层改名,得自己补上。
3.2 Windows 下 gmake 的两种造法
MinGW 自带的是mingw32-make.exe,而这套源码的 Makefile 和文档里写的都是gmake。两个名字指的是同一个程序,缺的只是一个拷贝:
# 方案一:复制一份改名,之后在任意 crowbar 目录里直接 gmake copy D:\MinGW\bin\mingw32-make.exe D:\MinGW\bin\gmake.exe # 方案二:不改名,构建时直接写全名 cd /d D:\work\crowbar_book_0_1 D:\MinGW\bin\mingw32-make.exe方案一的好处是跟源码里的命令完全对齐,之后照着书里敲gmake就能跑,不用每次记住长名字;方案二的好处是不动工具链目录,适合共用环境。我一般选方案一,反正只是多一个几 MB 的副本。
进目录之后,第一次构建大概率会看到一长串 gcc 命令行刷屏,那是 Makefile 在逐个编译.c。看到最后一行是cd ./memory; gmake并且没有 Error,就说明主程序已经生成了。这一步如果报Makefile:xx: recipe for target 'crowbar' failed,往下翻第一条 error,通常是某个.o没编出来,按第 5 章的排查顺序往后找。
3.3 Linux 下 make.sh 与直接 make 的差别
Linux 这条线简单得多,系统自带 GNU make,gmake和make是同一个东西,不需要任何改名。进目录之后:
# 进到具体版本目录,直接跑 make 就行 cd ~/crowbar_book_0_1 make # 想清干净重来用这个,比手动 rm 一堆 .o 稳 make clean && makemake.sh存在的意义主要是给完全不想看 Makefile 的人一个入口,内容通常就是切目录加 make。真正需要留意的是make clean——这套源码的中间文件(y.tab.c、y.tab.h、lex.yy.c、各种.o)散落在当前目录,删源码目录重来最保险,执行 clean 之后确认这几个文件确实消失了,再重新 make,能避免"改了 .y 文件但中间文件没重新生成"这种诡异现象。
Linux 上唯一容易翻车的地方是编码。源码是日文环境写的,注释里混着非 ASCII 字符,某些终端 locale 下 gcc 会抱怨。这种情况下用LANG=C make跑一遍通常就过去了,不影响生成的二进制。
4. 编译 Diksam:compiler 到 main 的目录迁移与 oniguruma 依赖
Diksam 比 Crowbar 复杂一个量级,它是一个带静态类型的语言实现,构建流程被拆成了两段式,而且从 0.3 开始引入了一个外部依赖库。四个版本里前两个和后两个的构建入口完全不在同一层,这是最容易让人卡住的地方——照着 0_1 的步骤去编 0_3,会在根目录下找不到 Makefile。
4.1 0_1/0_2 与 0_3/0_4 的构建入口不同
diksam_book_0_1和diksam_book_0_2的 Makefile 在compiler子目录下,进根目录直接 make 是没用的,会报No targets specified and no makefile found。正确路径是先下潜一层:
# 0.1 和 0.2 这样进,构建入口在 compiler 子目录 cd diksam_book_0_1/compiler gmake # Windows make # Linux # 0.3 和 0.4 的入口换到了 main 子目录 cd diksam_book_0_3/main gmake之所以换位置,是因为 0.3 之后整个工程被拆成了编译器前端、虚拟机、运行时几块,main才是最终链接可执行文件的地方。判断方法很简单:进目录先dir或ls看有没有 Makefile,没有就往下找一层,别急着怀疑工具链。
4.2 oniguruma 5.9.4 在 Linux 下的编译
Diksam 0.3 之后用正则库 oniguruma 做字符串相关功能,版本锁定在 5.9.4。Linux 下走标准的三步:
# 解包,源码包名是 onig-5.9.4.tar.gz tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 # 标准 autoconf 流程,装到系统默认前缀 ./configure make sudo make install./configure跑完会刷一大堆checking for ...,最后打印creating ./config.status和若干creating Makefile,都出现才算成功。中间如果有某个checking是 no,一般不影响本体编译,除非是alloca或memcmp这类基础函数。
make install需要写/usr/local/lib和/usr/local/bin,权限不够就加 sudo。装完之后ldconfig刷一下动态库缓存,再编译 Diksam 时链接器才找得到libonig。
4.3 Windows 下走 Cygwin 编 onig 再手工安置 libonig.a
Windows 这条线是全文最绕的一段。MinGW 没有make install这套东西,./configure生成的 Makefile 里那几条 install 规则跑不通,硬跑会得到一堆No such file or directory。这套资料给的做法是借 Cygwin 的 bash 和 make 把库编出来,再手工把静态库挪到 MinGW 认得的位置。
Cygwin 安装器分 32 位和 64 位两个 exe,跟你的 MinGW 位数对齐,混用会在链接阶段报架构不匹配。安装时选Install from Internet,Root Directory 和 Local Package Directory 保持默认即可,连接方式选Direct Connection。包选择页是关键:bison(部分版本归在 Interpreters 分类下)、m4、make 三个必须勾上,缺 m4 会让 bison 在 configure 阶段挂掉。
装完打开 Cygwin 终端,把源码包放到一个 Cygwin 能访问的路径(比如C:\),然后:
# Cygwin 终端里执行,注意用的是 Cygwin 的 bash 而不是 cmd cd /cygdrive/c tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 ./configure makemake通过之后不要执行make install。真正需要的东西在.libs目录下:
# 进 .libs 目录,给静态库建索引,然后手工拷到 MinGW 的 lib 下 cd .libs ranlib libonig.a cp libonig.a /cygdrive/d/MinGW/lib/ranlib那一步是给静态库生成符号索引,缺了它链接时会报undefined reference,错误信息指向的函数明明存在,就是找不到,属于典型的黑匣子式报错。拷过去的libonig.a落到D:\MinGW\lib之后,Diksam 的 Makefile 里那条-lonig才能被解析到。头文件同理,把oniguruma.h之类的.h拷一份到D:\MinGW\include,编译阶段才不会报找不到头文件。
5. 踩坑排查:bison 空格路径、y.tab.h 缺失与换行符
这套源码的报错有个共同特点:现象和根因隔得很远。下面五条是我和周围人实际撞过的,按「现象 → 原因 → 解决」写清楚,遇到时可以直接对照。
5.1 bison 报 m4: cannot open `Files'
现象:执行 gmake 之后,屏幕上刷出一串m4: cannot open 'Files': No such file or directory、m4: cannot open '(x86)\GnuWin32\Bison/share/bison': No such file or directory,最后以y.tab.h构建失败告终。
原因:bison 装在C:\Program Files (x86)\GnuWin32这种带空格的路径下,它调用 m4 时把路径拼进命令行,空格把参数切成了两段,m4 只拿到前半截,于是去找一个叫Files的文件。
解决:卸载重装到D:\GnuWin32\bison,或者手工把整个目录搬到无空格路径,再改 PATH 指向新位置。改完记得新开窗口,旧窗口的环境变量不会自动更新。
5.2 gcc 报 y.tab.c 或 y.tab.h 找不到
现象:gcc: y.tab.c: No such file or directory,紧跟着mycalc.l:3:19: error: y.tab.h: No such file or directory。
原因:.l文件里#include "y.tab.h"是硬依赖,而y.tab.h只能由 bison 处理.y文件后生成。bison 那一步失败了(或者是根本没跑),gcc 就直接找不到头文件。这是一条连锁反应,真正的根因在上一条。
解决:单独手工跑一次 bison 看它自己的报错:
# 单独跑 bison,把它的原始报错暴露出来 bison --yacc -dv crowbar.y # 成功的话当前目录应该多出两个文件 dir y.tab.c y.tab.h--yacc是让它按 yacc 的兼容模式输出文件名,-dv额外生成.output和.h。两个文件都出来了,再回去 gmake 就没问题。
5.3 mingw32-make 报 CreateProcess failed
现象:process_begin: CreateProcess(NULL, bison --yacc -dv crowbar.y, ...) failed,后面跟一句系统找不到指定文件的乱码提示。
原因:Makefile 里写了bison,但当前 PATH 里没有 bison,系统找不到这个可执行文件。另一种情况是 PATH 里有,但名字对不上或路径写错了。
解决:在同一个 cmd 窗口里直接敲bison --version确认能不能找到。找不到就把D:\GnuWin32\bison\bin补进 PATH;能找到还报错,检查 Makefile 里那条规则写的是不是绝对路径,绝对路径里的盘符和目录名有没有改过。
5.4 gmake 报 No targets specified and no makefile found
现象:进到diksam_book_0_3根目录执行 gmake,立刻返回gmake: *** No targets specified and no makefile found. Stop.
原因:Makefile 不在这一层。Diksam 0.3 之后构建入口挪到了main子目录,根目录只有源码和文档。
解决:往下找一层。判断规则是dir /s Makefile或者ls */Makefile,看 Makefile 到底躺在哪,cd 进去再跑。这个问题在不熟悉目录结构时几乎必踩一次,养成"先看有没有 Makefile 再 make"的习惯能省不少时间。
5.5 Cygwin 下 make install 报 ranlib 找不到目标
现象:D:\MinGW\bin\ranlib.exe: '/usr/local/lib/libonig.a': No such file,后面跟着Makefile:415: recipe for target 'install-libLTLIBRARIES' failed。
原因:Cygwin 里的/usr/local/lib是 Cygwin 自己的虚拟路径,MinGW 的ranlib.exe是原生 Windows 程序,不认这个路径,于是找不到目标文件。
解决:别指望 install 规则,改成手工安置。进.libs目录对libonig.a单独执行ranlib,再拷到D:\MinGW\lib,如 4.3 节所示。这条路绕开了路径转换,稳定得多。
6. 装完怎么验证:用 test.crb 和 mycalc 把整条链路跑一遍
编译通过不等于能用。这套源码里每个版本目录都带测试用例或者能自己造一个,验收到这一步才算真装好。
Crowbar 的验证最直接,crowbar_book_0_1目录下通常有test子目录,里面放着.crb脚本:
# Windows crowbar.exe test\test.crb # Linux ./crowbar test/test.crb能正常输出脚本里的计算结果,说明词法、语法、执行三层都通了。如果在 Windows 上看到类似第 5 行: 非法字符(0x0d)的报错,不是程序坏了,是test.crb用了 CRLF 换行,而 Crowbar 的词法分析器只认 LF。Windows 上编辑过的源码文件经常带这个尾巴,转一下就好:
# Git Bash 或者 Cygwin 终端里都行 dos2unix test/test.crbmycalc 系列的验证重点是 yacc/lex 这条链路的联动。它不像 Crowbar 有一键构建,得手工跑三步:
# 第一步:bison 生成语法分析器,--yacc 保证输出文件名是 y.tab.c/h bison --yacc -dv mycalc.y # 第二步:flex 生成词法分析器 flex mycalc.l # 第三步:链接,-lfl 或 -ll 指向 flex 的库 gcc y.tab.c lex.yy.c -o mycalc -lfl三步任何一步没产物,后面的命令就必然失败——这正是 5.2 那条坑的成因。y.tab.c、y.tab.h、lex.yy.c三个中间文件在目录里凑齐了,再执行./mycalc输入一个表达式,能算出结果就算闭环。
| 验证项 | 命令 | 通过标准 |
|---|---|---|
| gcc 可用 | gcc -v | 打印版本号 |
| bison 可用 | bison --version | 打印版本号,无 m4 报错 |
| flex 可用 | flex --version | 打印版本号 |
| Crowbar 编译 | gmake/make | 目录下出现 crowbar 可执行文件 |
| Crowbar 运行 | ./crowbar test/test.crb | 输出正常结果,无 0x0d 报错 |
| Diksam 链接 | 在 compiler 或 main 下gmake | 无-lonig相关 undefined reference |
| mycalc 链路 | 三步手工构建 | 生成可执行的 mycalc |
这套东西真正麻烦的地方不在技术难度,而在报错信息全是隔了一层的间接信号:bison 路径带空格报的是 m4 找不到文件,y.tab.h 缺了报的是 gcc 找不到头文件,onig 没 ranlib 报的是链接期找不到函数。从那以后我每次装完这套源码,都强制走一遍dir y.tab.c y.tab.h、where gcc、./crowbar test/test.crb三连,把中间文件、工具链来源和运行时输出一次确认掉,再去碰具体代码。资源包里这几个目录都是现成的,解压之后按上面的路径直接编即可,希望帮到你。
本文还有配套的精品资源,点击获取