☰
自制编程语言环境搭建实战:工具链安装与排错指南
2026/10/17 22:16:03 网站建设 项目流程

简介:面向编程语言爱好者与入门学习者,这份 PDF 资料系统梳理自制编程语言的核心概念,涵盖语言设计原则、语法语义定义、编译器与解释器实现,以及基于 yacc/lex、bison/flex 和 MinGW 的构建方法。书中结合 Crowbar、Diksam 两个实际语言项目,完整演示从词法分析、语法分析到运行时管理的流程,并对比解释执行与编译执行的差异;同时给出 Linux 与 Windows 下开发工具链的配置思路,例如 MinGW 与 bison/flex 的搭配使用,能够帮助读者避开环境搭建的常见障碍,适合希望动手实现小型编程语言的开发者阅读。资源共 1 个文件,类型为 PDF,压缩包大小 2.38MB,轻量便于离线学习。目前已有 870 人学习下载,作为入门级参考材料具备一定热度。通过该书还可了解设计中的迪米特法则、单一职责原则等要点,以及现成语言项目 Crowbar/Diksam 的整体结构,是一份覆盖设计、实现到使用的实用指南。

1. 自制编程语言环境:装好一套工具链比写解释器还难

拿到《自制编程语言》的配套源码包,大多数人以为解压后 make 一把梭就能跑通。实际体验是:bison 没装,y.tab.c 不生成;Windows 路径带空格,m4 先炸给你看;Linux 上好不容易编译完,又因为 CRLF 换行符让测试脚本直接报 0x0d。我排查过好几轮这类问题,十次有八次不是语言本身写错,而是工具链没配对。这套源码横跨 calc、mycalc、llparser、crowbar、Diksam 多个版本,依赖 yacc/lex 与 bison/flex,又涉及 MinGW、Cygwin、oniguruma 的取舍,正好适合正在啃编译原理、毕设选编译器方向、或者想把书里每章示例都跑一遍的开发者。这篇就按「装什么 → 怎么装 → 坏了怎么救」的顺序,把整条路走通。

2. 源码包与版本选择:crowbar 0.1 到 0.4、Diksam 0.1 到 0.4 各有什么差别

2.1 先认清楚源码包里每一层是什么

解压后第一眼看到一堆目录,别急着进某个目录敲 make。每个目录对应书中一个阶段,语言实现方式完全不同,依赖也不一样。

目录类型实现方式适合看什么
calc计算器雏形yacc/lex最小的词法 + 语法组合
mycalc完整计算器yacc/lex表达式求值、变量、错误处理
mycalc_ex扩展版计算器yacc/lex变量赋值等扩展语法
llparser手写递归下降解析器纯 C不用 yacc 怎么写解析器
llparser_ex手写解析器扩展版纯 C手写解析器的扩展思路
crowbar_book_0_1 ~ 0_4脚本解释器yacc/lex + C解释器从 0.1 到 0.4 的演进
diksam_book_0_1 ~ 0_4编译器实现yacc/lex + C编译流程、虚拟机、后端优化

crowbar 0.1 是最小可运行的解释器,0.2 开始引入 GC,0.3 支持数组和对象等特性,0.4 加入对 oniguruma 正则库的依赖。Diksam 各版本是从「能跑通」到「编译流程更完整」的演进,0.1、0.2 的构建入口在 compiler 子目录而不是顶层,这个后面会专门说。版本之间不是简单替代码,而是同一门语言逐步长全的过程,建议按顺序看,而不是只拿最后一个版本。

2.2 依赖工具矩阵:bison/flex、MinGW、Cygwin、oniguruma 怎么取舍

这套源码的核心依赖不是 GCC,而是 bison/flex。.y文件交给 bison 生成语法分析器,.l文件交给 flex 生成词法分析器,两者分别产出 y.tab.c 和 lex.yy.c,再由 C 编译器链接成可执行文件。

平台编译器 / make词法语法生成器正则库
Linuxgcc + makebison + flex + m4oniguruma 5.9.4 源码安装
Windows + MinGWMinGW + mingw32-makeWindows 版 bison/flex 完整包MinGW bash 里源码安装
Windows + CygwinCygwin 的 gcc + makeCygwin 包管理器装 bison/m4Cygwin 里源码安装
macOSclang/gcc + makeHomebrew 装新版 bison/flexHomebrew oniguruma 或源码安装

我的建议是新手走 MinGW。原因只有一个:MinGW 编出来的 exe 不依赖 cygwin.dll,拷到别的 Windows 机器上也能直接跑;Cygwin 编出来的程序运行时必须带着 cygwin.dll,否则一启动就报缺 DLL。但如果源码目录里有 configure 脚本,要走./configure && make这套 autotools 流程,Cygwin 往往更顺,MinGW 的 bash 环境在这种场景下会多一些莫名其妙的小毛病。

2.3 make 工具名差异:mingw32-make、gmake、make 真不是一回事

很多 Windows 上的报错其实死在 make 工具名上。Linux 和 Cygwin 里的命令叫 make,MinGW 安装后提供的是 mingw32-make.exe,并不叫 make。有些教程或 Makefile 里写的是 gmake,那是 GNU make 的别名,Windows 默认没有这个命令。

我一般会做一次复制,把 MinGW 的 make 复制成 make.exe 和 gmake.exe,省得每次敲全名:

copy C:\MinGW\bin\mingw32-make.exe C:\MinGW\bin\make.exe copy C:\MinGW\bin\mingw32-make.exe C:\MinGW\bin\gmake.exe make --version

复制而不是改名的原因是:MinGW 安装器没有重命名选项,直接复制不会破坏原有的版本身份,又能让 Makefile 里的 make 和 gmake 都能被 cmd 找到。注意 Diksam 0.1、0.2 版本的构建入口在 compiler 子目录,别在顶层目录敲 make 然后看到 No makefile found 就以为源码坏了,先进子目录看一眼。

3. Windows 下的安装:MinGW 与 bison/flex 参数怎么配才不翻车

3.1 MinGW 安装器:组件勾选与 PATH 设置

先运行安装器 mingw-get-setup.exe,安装目录我一般选 C:\MinGW,也可以放到 D:\MinGW,但记住这个路径,后面配 PATH 要用。安装器打开后是 MinGW Installation Manager,不是直接装完就结束,还要勾组件。

需要勾两个:mingw32-base 和 mingw32-make。前者是 GCC 基础工具链,后者是 make 工具。如果后面要编 C++ 相关代码,顺手勾 mingw32-gcc-g++ 也行。勾完后在 Installation 菜单里点 Apply Changes,等它下载并应用组件。这一步慢,在线拉取经常要等一阵。

验证安装是否进入 PATH:

where gcc gcc --version mingw32-make --version

where能找到路径说明 bin 目录已生效。如果没有,手动追加 PATH:

setx PATH "%PATH%;C:\MinGW\bin"

但这里有个坑:setx 对环境变量长度敏感,PATH 本来就长的话,setx 可能截断后面的值。装过很多开发工具的人 PATH 往往超过一两千字符,我一般建议直接去系统属性里编辑 PATH,而不是用 setx 拼接。

3.2 bison/flex 的 Windows 完整包:目录别带空格

bison 和 flex 在 Windows 上没有像 MinGW 那样的统一安装器,常见做法是从第三方二进制仓库下载完整包,也就是带 exe、dll、m4 的那一版完整包,而不是只下载 exe。解压到 C:\GnuWin32 这类无空格路径,然后追加 PATH:

setx PATH "%PATH%;C:\GnuWin32\bin"

验证命令:

bison --version flex --version m4 --version

三个命令都能输出版本才说明完整包可用。这里最容易被忽视的是 m4。bison 运行时需要 m4 处理宏文件 m4sugar.m4,如果完整包里没带 m4,bison 会在生成解析器时突然报错。这也是为什么我不建议单独拿 bison.exe 出来用,而是整个完整包一起解压。

注意:bison 和 flex 的安装目录绝对不能是 C:\Program Files (x86) 这种带空格的路径。原因在第五章的 m4 报错里具体展开,这里先记住结论。

3.3 从 make 到 y.tab.h:先让 bison 单独跑通

进入源码目录直接敲 make,常见的失败场景是:

cd C:\crowbar_book_0_1 C:\MinGW\bin\mingw32-make.exe

报错类型千奇百怪,但根源往往只有一个:bison 没有成功执行,它该生成的 y.tab.c 和 y.tab.h 没出现。文件都没生成,后面的 gcc 当然找不到 y.tab.c。

这种情况下我会先手动跑一次 bison,把 Makefile 这层黑匣子拆开:

bison --yacc -dv crowbar.y dir y.tab.c y.tab.h

解释一下参数:--yacc让 bison 输出兼容 yacc 的文件名,-d生成头文件 y.tab.h,-v生成详细的语法状态文件 .output,排错时很有用。看到 y.tab.c 和 y.tab.h 都出现后,再回到 make,后续的 gcc 编译步骤就会自动接上。

如果手动 bison 也报错,那问题基本就是 PATH 没生效,或者 m4 缺失,回上一节检查。

3.4 一次完整的编译命令流与 CFLAGS 参数说明

把整个流程固定成一个批处理脚本最省心:

@echo off set PATH=C:\MinGW\bin;C:\GnuWin32\bin;%PATH% cd /d C:\win_sjis\crowbar_book_0_1 mingw32-make.exe clean mingw32-make.exe

第一行把 MinGW 和 GnuWin32 的 bin 临时加进当前终端 PATH,不污染全局。第二行进入源码目录,注意 /d 参数是切换盘符用的,直接 cd 在 C 和 D 盘之间切换会失效。第三行 clean 是清理上次失败留下的半成品,最后再完整编译。

Makefile 里能看到一组固定的编译参数:-Wall -Wswitch-enum -ansi -pedantic和-DDEBUG。-Wall 打开常见警告,-pedantic 强制 ANSI C 标准检查,-Wswitch-enum 对枚举 switch 做严格匹配检查。-DDEBUG会启用源码里的调试输出,想观察解释器内部执行过程就保留它;如果要测性能或让输出干净些,把这一项从 CFLAGS 里去掉再 make clean && make。

3.5 什么时候该选 Cygwin

Cygwin 适合那种「源码是按 Linux 习惯组织」的目录。比如要跑./configure && make,或者 Makefile 里大量使用 bash 语法,MinGW 的 cmd 环境下很容易挂在奇奇怪怪的地方。Cygwin 安装器区分 32 位和 64 位,64 位系统就用 setup-x86_64.exe,安装方式选 Install from Internet,Root Directory 和 Local Package Directory 按向导默认即可。需要勾的包:Interpreters 分类里的 bison 和 m4,Devel 分类里的 make 和 gcc。

装完打开 Cygwin64 Terminal,里面就是 bash 环境,操作方式和 Linux 基本一致。但注意 MinGW 和 Cygwin 同时装的情况下,PATH 先后顺序决定实际调用哪一套 bison 和 make。我习惯在当前终端里临时调整 PATH,而不是全局改,避免两个环境的工具互相干扰。

4. Linux 与 macOS 下的安装:configure/make 之外的几个必踩点

4.1 Linux 依赖:build-essential 与 bison/flex/m4 一次装齐

Linux 是这套源码最顺的平台,依赖装齐后基本一把过。Ubuntu/Debian 系的命令:

sudo apt-get update sudo apt-get install -y build-essential bison flex m4

build-essential 提供 gcc 和 make,bison/flex 负责生成解析器和词法器,m4 是 bison 运行时的宏处理器。装完进源码目录:

cd crowbar_book_0_1 make clean make

Linux 下的 make 直接可用,不需要复制改名。如果 make 报错说找不到 y.tab.c,同样先手动跑bison --yacc -dv crowbar.y看输出。注意较新的 bison 3.x 对老源码有时会报 shift/reduce 冲突警告,但通常不影响生成;如果报的是语法错误级别的问题,再考虑换 bison 2.7 系列。

4.2 oniguruma 5.9.4 源码安装:configure 到 ranlib 的完整路径

crowbar 0.4 之后的正则支持依赖 oniguruma,推荐版本是 5.9.4。源码安装流程:

tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 ./configure make make install

configure 阶段会输出一堆 checking,诸如checking for alloca... yes、checking for working memcmp... yes、checking for prototypes... yes。看到这些不用慌,是正常的 autotools 探测;最后出现creating Makefile、creating config.h、creating onig-config才算配置成功。make 完成后核心产物是 .libs/libonig.a 静态库,make install 默认装进 /usr/local/lib 和 /usr/local/include。

如果系统里已经通过 apt 装过 libonig-dev,源码版 5.9.4 的头文件可能和系统包冲突。我一般用独立 prefix 隔离:

./configure --prefix=$HOME/opt/onig make make install

编译 crowbar 0.4 时再用 CFLAGS 和 LDFLAGS 指过去,避免污染系统目录。

4.3 macOS 的 bison 版本坑与 y.tab.h 缺失

macOS 上最容易翻车的是系统自带 bison 太老。有开发者在 MacBook Air 上编译 mycalc 时遇到:

cc lex.yy.c -o examplel -ll mycalc.l:3:10: fatal error: 'y.tab.h' file not found

原因不是代码问题,而是 bison 没先生成 y.tab.h。macOS 自带 bison 还在 2.3,很多-d生成头文件的兼容行为跟新版不一致,建议用 Homebrew 装新版:

brew install bison flex export PATH="/opt/homebrew/opt/bison/bin:$PATH"

Intel Mac 的路径是 /usr/local/opt/bison/bin,M 系列是 /opt/homebrew/opt/bison/bin,拿不准就用brew --prefix bison查。装完手动生成再编译:

bison -dy mycalc.y flex mycalc.l cc lex.yy.c y.tab.c -o mycalc -ll

-dy里的-d生成 y.tab.h,-y让输出文件名兼容 yacc 习惯;flex 生成 lex.yy.c;-ll链接 flex 运行库,某些系统上是-lfl。顺序不能乱:先 bison 后 flex,头文件先齐了,lex.yy.c 里的#include "y.tab.h"才不会扑空。

4.4 Windows 里用 bash 装 oniguruma:make install 之后记得补 ranlib

在 Windows 上装 oniguruma,我通常把压缩包放到 C 盘根目录,然后进 MinGW 或 Cygwin 的 bash 环境操作:

tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 ./configure make

注意这一步在 MinGW 的 bash 下通常能顺利走完,但 make install 有时会出幺蛾子。报错典型是这样:install 过程中找不到 .libs/libonig.a,或者 ranlib 执行时目标文件不存在。解决办法是手动补一刀:

cd .libs ranlib libonig.a cp libonig.a /usr/local/lib/

libonig.a 是静态库,ranlib 负责生成索引,链接阶段要靠这个索引找符号。MinGW 的 make install 有时没把这一步做完,手动补上就能让后续链接正常。这是我每次在 Windows 上编译 crowbar 0.4 之前的固定动作。

5. 编译排错避坑:五条高频报错的根源与处置

5.1 m4 报错:路径被空格拆开

现象:

m4: cannot open `Files': No such file or directory m4: cannot open `(x86)\GnuWin32\Bison/share/bison': No such file or directory m4: cannot open `D:\Program': No such file or directory

原因:bison 安装路径带空格,比如 C:\Program Files (x86)\GnuWin32。bison 运行时调用 m4 处理 share/bison 下的宏文件,路径里有空格时,m4 把路径按空格拆成多个参数,于是出现 cannot open `Files' 这类诡异报错。

解决:把 bison 重装到无空格目录,C:\GnuWin32 或 C:\tools\bison 都行。同时检查环境变量 BISON_PKGDATADIR,如果指向带空格路径也要改。改完重新打开终端再跑,因为旧终端里缓存的 PATH 不会自动更新。

5.2 process_begin 报错:bison 不在 PATH 里

现象:

process_begin: CreateProcess(NULL, bison --yacc -dv crowbar.y, ...) failed. make (e=2): 系统找不到指定的文件。 Makefile:30: recipe for target 'y.tab.h' failed mingw32-make: *** [y.tab.h] Error 2

原因:Makefile 里的 bison 命令是在子进程里执行的,cmd 只有在 PATH 里能搜到 bison.exe 才能创建进程。报 e=2 说明系统压根没找到这个可执行文件,不是 bison 语法出错。

解决:先确认where bison能输出路径,bison --version能正常打印。如果安装了但找不到,说明 PATH 没追加或没在新终端里生效。另外注意如果同时装了 Cygwin 和 GnuWin32 两套 bison,PATH 顺序决定调用哪一套,混用容易出现行为不一致。

5.3 y.tab.c 缺失与 y.tab.h not found

现象:

gcc: y.tab.c: No such file or directory mycalc.l:3:19: error: y.tab.h: No such file or directory

原因:这是 5.2 的连锁反应。bison 没跑成功,y.tab.c 和 y.tab.h 都没生成,gcc 编 .y 产物时扑空,flex 生成的 lex.yy.c 里#include "y.tab.h"也跟着失败。头文件是 .l 和 .y 之间约定的接口,少了一环后面全是连锁编译错误。

解决:手动执行bison --yacc -dv mycalc.y,确认 y.tab.c、y.tab.h 都在当前目录,再把 make 重新跑起来。如果手动执行时 bison 本身报语法错误,那才需要回头查 .y 文件的语法,多数情况下错误都出在工具链上而不是源码上。

5.4 链接失败:cannot find -lmsvcp60

现象:

/usr/bin/ld: cannot find -lmsvcp60

原因:-lmsvcp60 是微软 C 运行库的链接参数,这个库只存在于 Windows/MSVC 环境。Linux 或 macOS 的链接器当然找不到。出现这个报错,说明这份源码的 Makefile 是面向 Windows 侧的版本,或者 Makefile 里残留了 Windows 链接参数。类似情况还出现在某些源码里写死-lm之外还带平台相关库的时候。

解决:优先换官方注明支持 Linux 的版本目录,很多情况下不同版本号的 Makefile 已经修过平台差异。如果只有这一份源码,去 Makefile 的 LIBS 或 LDLIBS 变量里删掉-lmsvcp60再 make。但要注意如果源码里还包含 windows.h 等头文件依赖,就不是删一个链接参数能解决的,这时候改用 Cygwin 环境整体编译更实际。

5.5 CRLF 换行符:测试脚本报 0x0d

现象:

[root@localhost crowbar_book_0_1]# ./crowbar test/test.crb 5: 错误 (0x0d)

原因:test.crb 是在 Windows 环境下编辑的,换行符是 CRLF。Linux 下的词法分析把行尾的\r(0x0d)当成非法字符,于是每个换行都报错。这不是语法问题,是行尾格式问题。

解决:把测试脚本转成 LF 再跑。

dos2unix test/test.crb # 或者不装 dos2unix 时用 sed sed -i 's/\r$//' test/test.crb

整个目录批量处理:

find . -name "*.crb" -exec sed -i 's/\r$//' {} \;

处理完用file test.crb确认输出里不再带 CRLF。macOS 上如果遇到类似奇怪解析错误,也用同样的思路先查换行符。

此外还有一条不大不小的坑:MinGW 下 make 报No targets specified and no makefile found,八成是当前目录没有 Makefile,或者进错了子目录。先dir Makefile看一眼再决定下一步,别急着怀疑工具链。

6. 装完怎么验证:把示例脚本一步跑通

6.1 先验证 mycalc:工具链的最小闭环

mycalc 是依赖最少的一个目录,只有 bison/flex 和 C 编译器,最适合当安装验证。

cd mycalc make clean && make ./mycalc

交互式输入1+2*3;,能输出 7 就说明 lexer、parser、求值器都通了。mycalc 是最小闭环,它跑不通时别急着去折腾 crowbar,先在这里找问题。

6.2 用 crowbar 自带测试脚本验证解释器

crowbar 的验证要完整得多,涵盖字符串、数组、控制流等语法:

cd crowbar_book_0_1 make clean && make dos2unix test/test.crb ./crowbar test/test.crb

第一行清理上一次的中间产物,第二行重新编译,第三行是处理 CRLF,最后运行测试脚本。如果编译时保留了-DDEBUG,运行过程会输出更多内部状态,方便确认词法分析、语法树构建、执行器三个阶段都正常工作。从 crowbar 0.2 开始有 GC,可以多跑几个带字符串和对象分配的测试,观察内存回收是否正常。

6.3 Diksam 的编译路径验证

Diksam 是编译器路线,验证方式和解释器不同:

cd diksam_book_0_4 make clean && make ./diksam samples/xxx.dkm

Diksam 会先解析源码,编译成中间表示,再执行。源码包自带样例就直接跑样例,没有就手写一段 if 和 while,确认前端解析和后端执行都正常。早期版本 0.1、0.2 的构建入口在 compiler 子目录,先进cd compiler再看 Makefile。

6.4 从零重建:唯一的强制验证手段

有开发者在 Windows 下遇到过编译成功一次之后,修改源码再 make 一直提示 up to date 的情况,其实是 make 的时间戳判断出了问题。我现在的习惯是:每次拿到新源码包,先看 Makefile 里的 clean 目标,然后强制从零走一遍:

make clean find . -name "*.o" -delete make

只有 clean 后重建成功,才说明工具链是真的没问题,而不是被缓存掩盖了。从那以后每次装这类带 bison/flex 的源码包,我都强制走一遍从零重建,确认输出可执行文件后再去读代码。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询