简介:面向编程语言学习者的自制语言开发资料,系统讲解编程语言从设计、实现到使用的完整流程,涵盖语法语义设计、编译器与解释器构建,并以 crowbar 和 Diksam 两套自制语言为例,展示基于 yacc/lex、bison/flex 以及 MinGW 工具链的具体实现思路,同时覆盖 Linux 和 Windows 环境下的跨平台开发要点。资源以单个 PDF 文件呈现,大小仅 2.38MB,内容结构清晰,便于集中阅读与检索,已有 870 人浏览学习。读者可从中深入了解词法分析、语法分析、运行时环境等关键模块,并跟随 crowbar 与 Diksam 从 0.1 到 0.4 各版本的演进,看到从简单计算器到完整语言的逐步实现过程。无论是入门理解编译原理,还是动手设计自己的编程语言,这份资料都能提供较为系统的参考,尤其适合对语言设计感兴趣的开发者、计算机专业学生及相关技术爱好者。
1. 自制编程语言环境搭建:从工具链到能跑通 crowbar 与 Diksam
想跟着《自制编程语言》把 crowbar 和 Diksam 从源码编译出来,卡在 Windows 环境上的人不在少数。最常见的翻车现场是m4: cannot open 'Files': No such file or directory——明明装了 bison,一执行make就报路径被截断,这种玄学错误十有八九是工具链安装路径带空格惹的祸。还有人在 Linux 下编译 crowbar_book_0_1 时遇到(0x0d)解析错误,最后发现是 Windows 的 CRLF 换行符在捣乱。这套环境的难点不在语言本身,而在 yacc/lex(或 bison/flex)和 GCC 工具链的版本匹配与参数配置。本文面向卡在编译阶段的开发者,先把工具链选型和环境变量讲清楚,再逐步落到每个源码包的构建命令,最后给出验证跑通的方法。
2. 工具链选型:MinGW 与 Cygwin 怎么选,版本差异踩过的坑
2.1 两种 Windows 编译环境的核心差异
搭建这套自制语言环境,第一步不是急着下载源码,而是想清楚在哪个编译环境下构建。书中源码同时提供了 Linux 的make.sh和 Windows 的make.bat,但实际的构建脚本在 Windows 下依赖的是 Unix 风格的工具链。常见的做法有两种:MinGW 和 Cygwin。
MinGW 提供的是原生的 Windows 编译工具链,编译出来的 exe 不依赖额外的 DLL。但它的问题是默认不带 make,需要单独装 mingw32-make,而且 bison/flex 这些生成器通常不在 MinGW 的默认安装包里。Cygwin 则是完整的 Unix 模拟环境,自带 bison、m4、make,编译方式几乎和 Linux 一致,代价是生成的 exe 依赖 cygwin.dll。
我一般建议新手直接走 Cygwin 路线。原因是书中crowbar_book_0_2之后的版本在链接阶段会遇到-lmsvcp60这类 MSVC 运行库的问题,MinGW 下处理起来要手动找库文件,Cygwin 下反而顺畅。MinGW 适合那些已经装了完整工具链、只想用mingw32-make.exe跑 Makefile 的熟手。
2.2 源码包的结构与构建顺序
源码压缩包里按版本组织目录,核心是calc、mycalc、llparser三个前置练习,然后是crowbar_book_0_1到crowbar_book_0_4,最后是diksam_book_0_1到diksam_book_0_4。构建顺序必须从crowbar_book_0_1开始,不要一上来就编 Diksam——后者依赖前者积累的代码基础。
每个版本的 Makefile 针对 Linux 和 Windows 做了区分,但 Windows 下要手动指定生成器。比如crowbar_book_0_1用 yacc/lex,而crowbar_book_0_3起引入了 GC(垃圾回收)逻辑,构建参数会多出-DDEBUG等选项。Diksam 从diksam_book_0_3开始改用手写的 main 函数,不再完全依赖 yacc 生成的y.tab.c,这会导致 Makefile 的目标依赖关系发生变化。
# Linux 下编译某个版本的标准流程 cd crowbar_book_0_1 chmod +x make.sh ./make.sh这个脚本做的事情是调用gcc编译lex.yy.c、y.tab.c以及main.c、interface.c等源文件,最后链接成crowbar可执行文件。注意lex.yy.c和y.tab.c是 flex 和 bison 生成的中间文件,Makefile 里会先执行bison --yacc -dv crowbar.y和flex crowbar.l来生成它们。
2.3 正则库 oniguruma 的版本选择
crowbar_book_0_4引入了正则表达式支持,依赖 oniguruma 库。书中提供的版本是 5.9.4,这是一个比较关键的版本——太新的版本在 configure 阶段可能因为编译器标准问题报错,太老则缺少部分正则语法。
Linux 下安装 oniguruma 是标准的三步走:
wget http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.4.tar.gz tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 ./configure make make install这里的configure会检测当前环境是否具备 ANSI C 编译器、alloca 函数和标准头文件。如果之前装过其他版本的 oniguruma,建议先make uninstall清理干净再装 5.9.4,否则链接时可能出现符号冲突。装完后make install默认把库文件放到/usr/local/lib,记得确认这个目录在动态链接器的搜索路径里。
Windows 下用 Cygwin 编译 oniguruma 时,我踩过一个坑:在make install阶段报ranlib: '/usr/local/lib/libonig.a': No such file。原因是 Cygwin 的安装路径里包含了 Windows 路径转换问题,需要进入.libs目录手动执行ranlib libonig.a,再把生成的静态库拷贝到/usr/local/lib。
3. Windows 下 MinGW 完整安装:从 mingw-get 到 make 的 PATH 配置
3.1 下载与安装 mingw-get-setup.exe
MinGW 的安装方式是下载mingw-get-setup.exe引导程序,然后通过它在线拉取需要的组件。书中提到的官方源是http://www.mingw.org/,但实际下载时经常跳转到 SourceForge 的镜像,需要手动选一个可用镜像。
安装过程的要点在 Installation Directory 这一步。很多人习惯默认装到C:\MinGW或者D:\Program Files (x86)\MinGW,后者会埋下路径带空格的雷。我建议手动改成D:\MinGW这样的纯英文无空格路径,因为 bison 和 m4 在解析 include 路径时不会对空格做转义处理。
安装完成后打开 MinGW Installation Manager,在左侧勾选mingw32-base和msys-base,前者提供 GCC 编译器,后者提供 make 和其他 Unix 工具。注意要点击Mark for Installation后再点菜单栏的Installation -> Apply,否则只是标记不会真正下载。
3.2 把 make.exe 和 gcc.exe 加进 PATH
装完 MinGW 后,D:\MinGW\bin目录下会有mingw32-make.exe,这个文件的名字很有迷惑性——它不是make.exe,很多人在命令行里敲make发现提示找不到命令。解决办法有两个:一是把D:\MinGW\bin加入系统 PATH,二是把mingw32-make.exe复制一份改名为make.exe。
# 把 MinGW 加入 PATH(Windows 命令行) setx PATH "%PATH%;D:\MinGW\bin" # 验证 gcc 和 make 是否可用 gcc --version make --version如果通过setx设置后新开的命令行窗口仍然找不到命令,检查一下是不是用了set PATH=...而不是setx——前者只对当前窗口生效,后者才会写入系统环境变量。另外,如果系统装了 Git for Windows,它自带的/usr/bin/make.exe可能会和 MinGW 的冲突,PATH 顺序决定了先找到哪个。
3.3 用 Makefile 构建时的目标依赖问题
MinGW 下跑crowbar_book_0_1的 Makefile,经常遇到的目标依赖报错是make: *** No targets specified and no makefile found。原因很直接:当前目录没有Makefile文件。源码包里默认带的是make.sh和make.bat,Makefile是用make.bat里的命令从Makefile.in生成的,或者直接解压后就有——取决于你下的是哪个版本包。
D:\win_sjis\crowbar_book_0_1> D:\MinGW\bin\mingw32-make.exe bison --yacc -dv crowbar.y process_begin: CreateProcess(NULL, bison --yacc -dv crowbar.y, ...) failed. make (e=2): 系统找不到指定的文件。这段报错的根因不是 Makefile 本身有问题,而是 MinGW 的 make 在调用 bison 时,去 PATH 里找bison.exe但没找到。MinGW 默认不包含 bison 和 flex,它们属于 GnuWin32 项目,需要单独下载安装。所以顺序应该是:先装 bison/flex,再跑 make,否则 make 会在生成y.tab.c的第一步就失败。
4. bison 与 flex 的安装细节:GnuWin32 与 Cygwin 两条路线
4.1 GnuWin32 的 bison 与 flex 安装步骤
GnuWin32 项目提供了 Windows 原生版的 bison 和 flex,下载地址在http://gnuwin32.sourceforge.net/packages/bison.htm和http://gnuwin32.sourceforge.net/packages/flex.htm。要选Complete package, except sources这个安装包,它包含可执行文件和运行时 DLL,纯源码包没有 exe 不能用。
安装时同样要注意路径问题。GnuWin32 默认装到C:\Program Files (x86)\GnuWin32,这个路径在 bison 调用 m4 时会导致经典报错:
m4: cannot open `Files': No such file or directory m4: cannot open `(x86)\GnuWin32\Bison/share/bison': No such file or directory原因分析:bison 内部会调用 m4 来处理语法文件,m4 在构造路径时把带空格的路径按空格拆开了。解决方法是把 GnuWin32 重装到C:\GnuWin32或者直接手动把bison.exe、m4.exe和相关的 DLL 拷贝到一个纯路径目录下,再把这个目录加入 PATH。
也有读者反馈直接改了环境变量后仍然报同样的错,最后是把C:\GnuWin32\bin放到了 PATH 的最前面,同时把C:\Program Files (x86)\GnuWin32\bin从 PATH 里移除才解决。这说明 PATH 里有多个 bison 版本时,make 会按顺序找到第一个可用的,如果你的 PATH 里恰好有旧版 bison 就会先命中它,进而用不兼容的方式调用 m4。
4.2 Cygwin 路线的安装配置
如果不想跟 GnuWin32 的路径纠缠,第二条路是装 Cygwin。到官网下载setup-x86_64.exe(64 位系统),安装时选择Install from Internet,Root Directory 可以改成C:\cygwin64,Local Package Directory 是下载缓存的临时目录,随便设一个。
选包阶段需要勾选这三个组件:
Interpreters分类下的bisonDevel分类下的m4和makeDevel分类下的gcc-core(如果没装的话)
Cygwin 安装完成后,桌面上会出现Cygwin64 Terminal的快捷方式,打开就是 bash 环境。在这个终端里执行bison --version能直接看到版本号,说明工具链已经就位。此时进入源码目录运行./make.sh,构建过程中如果报cannot find -lmsvcp60,说明链接器在找 MSVC 的运行库——Cygwin 下需要在 Makefile 的LIBS变量里把-lmsvcp60换成-lonig(如果你在编 crowbar_0_4 或 Diksam)。
4.3 生成文件缺失时的手动处理流程
bison 和 flex 运行成功后会在源码目录生成y.tab.c、y.tab.h和lex.yy.c三个文件。如果 make 执行过程中途失败,这三个文件可能不会完整生成,后续 gcc 编译会报y.tab.h: No such file or directory。
# 手动重新生成(在源码目录下执行) bison --yacc -dv crowbar.y flex crowbar.l ls -la y.tab.c y.tab.h lex.yy.c这里bison --yacc -dv中的-d参数表示同时生成头文件y.tab.h,-v输出详细错误信息。如果第一步 bison 就报错,多半是crowbar.y里有语法冲突,这时候不要硬着头皮往下跑,先看报错行号定位是哪个规则写错了。flex 生成的lex.yy.c里如果报undefined reference to 'yywrap',需要在链接时加-lfl或者-ly依赖 flex 库。
5. 环境搭建常见问题排查:五个高发报错的处理记录
5.1 m4 路径空格报错
现象:执行make时出现m4: cannot open 'Files': No such file or directory,bison 完全无法运行。
原因:bison 调用 m4 时把安装路径按空格切片了,C:\Program Files (x86)\GnuWin32被拆成了C:\Program和Files两段。m4 无法解析带空格的文件路径。
解决:把 GnuWin32 重装到无空格路径,并确认 PATH 里没有第二个 bison 干扰。安装后用where bison和where m4检查当前生效的是哪个版本。
5.2 Linux 下解析 test.crb 报 0x0d
现象:在 Linux 下编译crowbar_book_0_1成功,但运行./crowbar test/test.crb时报第 5 行解析错误,错误码带(0x0d)。
原因:源码包中的test.crb是在 Windows 下打包的,换行符是 CRLF。crowbar 的解释器在读文件时把\r当成非法字符。
解决:用dos2unix test/test.crb转成 LF 换行,或者用sed -i 's/\r$//' test/test.crb手动去除。macOS 下如果遇到同样问题也适用此方法。
5.3 make 报错 recipe for target 'crowbar' failed
现象:MinGW 下编译进行到cd ./memory; gmake.exe时中断,Makefile 报recipe for target 'crowbar' failed。
原因:memory子目录有自己的 Makefile,它调用的是gmake.exe而不是make.exe。MinGW 默认只有mingw32-make.exe,没有名为gmake的入口。
解决:在D:\MinGW\bin下复制一份mingw32-make.exe并改名为gmake.exe,或者把 Makefile 里的gmake替换成mingw32-make。
5.4 oniguruma 安装后 ranlib 找不到静态库
现象:Cygwin 下make install执行到一半报ranlib: '/usr/local/lib/libonig.a': No such file。
原因:libtool 生成的.a文件位于.libs子目录,但make install阶段路径切换出了问题,ranlib 在/usr/local/lib下找不到实际的库文件。
解决:进入.libs目录手动执行ranlib libonig.a,再把libonig.a拷贝到/usr/local/lib,然后重新运行make install。
5.5 bison 不可用导致 y.tab.c 未生成
现象:make 报gcc: y.tab.c: No such file or directory,但crowbar.y明明存在。
原因:bison 未安装或 PATH 没配置好,make 在尝试生成y.tab.c的第一步就失败了,后续的 gcc 编译因为缺少中间文件而跟着报错。
解决:先单独执行bison --yacc -dv crowbar.y,确认能生成y.tab.c和y.tab.h后再执行 make。若 bison 本身报createprocess failed,优先排查 PATH 中的 bison 路径是否完整。
6. 装完怎么验证跑通:从 mycalc 到 Diksam 的分级测试
验证这套环境是否装对了,不能只停留在能编译出 exe 的层面。按照书中源码的进阶路线,我习惯分三个等级做验证,每通过一级才进入下一级,这样出问题能立刻定位是哪个环节挂了。
第一级验证是calc和mycalc,这两个是最简单的表达式计算器,用 yacc/lex 生成解析器后,通过 gcc 编译。mycalc的例子要生成y.tab.h,所以 bison 必须带-d参数。运行mycalc.exe后输入一个四则运算表达式,比如1+2*3,期望输出7——注意这里*的优先级应该高于+,如果你的结果输出9,说明语法文件的优先级规则没有生效。
# 在源码目录下验证 mycalc(Windows 命令行或 Cygwin 终端) echo "1+2*3" | ./mycalc正常输出是7,如果报lex.yy.c: fatal error: y.tab.h: No such file or directory,回到第 4 章重新生成中间文件。
第二级验证是llparser和crowbar_book_0_1。llparser是手写的 LL(1) 解析器示例,不依赖 yacc,能跑通说明你的 C 编译器本身没有问题。而crowbar_book_0_1编出的 crowbar 解释器需要执行一个包含变量的脚本test.crb:
./crowbar test/test.crb如果前面的换行符问题已经处理干净,这个脚本会输出一系列赋值和打印结果。看输出里有没有乱码或Segmentation fault,一旦出现段错误,优先检查 GC 相关的宏定义是否一致——crowbar_book_0_3以后引入了 GC,编译时漏了-DDEBUG会导致运行时崩溃而不是编译失败。
第三级验证是 Diksam 系列。diksam_book_0_1的 Makefile 目标依赖比较复杂,需要确认 make 能正确找到编译器生成器。如果diksam_book_0_2编译通过但运行时报Diksam ver.0.2 无法加载 .dkm 文件,多半是运行目录和工作目录不一致导致的相对路径问题。用./diksam test.dkm的完整路径方式运行即可。
最后我个人的习惯是写一个最小脚本把整个链路过一遍:从mycalc到llparser再到crowbar_book_0_4,每个都执行一次编译和运行,全程记录输出。环境配置这东西,重装一次成本太高,不如留一份完整的验证脚本,下次换机器时直接跑一遍就知道缺什么依赖。从那以后,我每次拿到新的自制语言源码,都会强制走一遍这条验证链,确保不是工具链的巧合掩盖了代码本身的问题。希望帮到你。
本文还有配套的精品资源,点击获取