尧图精选

自制编程语言源码包详解:从calc到Diksam的编译器成长路线

🕒 发布时间:2026/10/2 14:08:37 📁 来源:尧图网络
简介一份围绕《自制编程语言》整理的中文学习资料以PDF文档形式打包面向希望从零设计并实现编程语言的开发者、在校学生及编译技术爱好者。资料从语言的设计原则语法、语义、可读性入手逐步延伸到 yacc/lex、bison/flex、MinGW 等工具链的配置与使用完整覆盖词法分析、语法解析、代码生成到解释器/编译器落地也涉及运行时环境与内存管理等实现要点。全篇以 Crowbar 和 Diksam 两个自制语言项目为主线从简易计算器、mycalc 示例一路推进到带 GC垃圾回收的脚本引擎包含 Crowbar 0.1 至 0.4、Diksam 0.1 至 0.4 的迭代演示并给出各版本在 Linux 与 Windows 双平台下的编译运行思路便于按章节边读边练。压缩包仅1个PDF文件大小2.38MB体积小巧当前已有869人学习浏览适合想通过具体项目理解编译原理的入门与进阶读者。1. 自制编程语言的源码包从 calc 到 Diksam 这条成长线怎么用自学自制编程语言最容易死在词法分析和语法分析这一步龙书里的 LR 分析表还没看完人已经没耐心了。这套《自制编程语言》相关资料走的是反方向——直接摊开一套从零长起来的源码从 100 行不到的 calc 计算器到 yacc/lex 生成的 mycalc再到带 GC、数组、对象的解释器 crowbar最后是编译成字节码的 Diksam。它解决的不是“编译原理怎么考”而是“一门语言到底怎么实现”这个具体问题。适合谁想弄懂 yacc/lex 生成器产物的人想在 Windows/Linux 上把一个完整语言跑起来的人以及手头正缺一份能拆开读的解释器工程的人。这篇笔记按目录结构、环境搭建、常见报错、验证方法四件事把它拆开讲。2. 目录与工具链从 yacc/lex 生成器看 mycalc 和 crowbar 的依赖关系2.1 六个源码包的演进路线calc、mycalc、llparser、crowbar、Diksam 各看什么这套包不是“一个最终成品”而是作者把学习过程本身打包进来了。你按目录顺序读等于重新走一遍编译器的成长路线。我先把目录整理成一张对应表后面再逐个说目录核心机制适合谁读calc最简计算器不依赖 yacc/lex第一次接触“语言雏形”的人mycalc / mycalc_exyacc/lex 生成词法与语法分析器想弄清生成器产物的人llparser / llparser_ex手写递归下降解析器想摆脱生成器、理解本质的人crowbar_book_0_1 ~ 0_4完整脚本解释器含 GC、数组、对象、正则想读完整解释器执行模型的人diksam_book_0_1 ~ 0_4文本编译器 字节码 VM想读编译器完整流程的人calc 是最小可运行语言只有表达式求值没有语法生成器纯手写适合当天下午跑通。mycalc 引入了 yacc/lex这是第一道分水岭你得理解.y文件描述语法、.l文件描述词法然后由生成器帮你产出 C 代码。llparser 系列又往前走一步作者把语法分析器手写出来说明在生成器之外你还能用递归下降自己实现解析。到了 crowbar才是真正意义上的脚本语言解释器它有自己的执行循环、变量表、函数调用栈还会做字符串和正则匹配。Diksam 则把编译期和运行期彻底分开先编译成字节码再用虚拟机执行这已经是产品级架构的雏形。2.2 生成器选型为什么是 bison/flex以及先 bison 后 flex 的硬顺序yacc 和 lex 是 Unix 上古时代的经典组合bison 和 flex 是它们的 GNU 实现功能兼容但更活跃。这套源码里 Makefile 调用的就是 bison 和 flex如果你机器上只装了老 yacc/lex命令参数和产物文件名都会有差异。以 crowbar_book_0_1 为例生成关系是这样的# 语法文件 crowbar.y 交给 bison crowbar.y - bison --yacc -dv crowbar.y - y.tab.c y.tab.h # 词法文件 crowbar.l 交给 flex crowbar.l - flex crowbar.l - lex.yy.c # 生成产物和手写源码一起编译 y.tab.c lex.yy.c main.c interface.c execute.c ... - crowbar这里有个硬性顺序必须先跑 bison再跑 flex。原因是crowbar.l里#include y.tab.h而 y.tab.h 是 bison 生成的如果不先执行 bisonflex 生成的 lex.yy.c 引用头文件时会直接报错。--yacc参数让 bison 兼容传统 yacc 行为-d表示生成头文件-v生成语法分析状态机的报告文件 y.output。这三个参数组合在一起既保证行为一致又方便你翻 y.output 看 LALR 状态迁移。作者在 Makefile 里把这些写成目标依赖就是希望你别手动一条条敲。2.3 oniguruma 依赖crowbar 的正则支持是外挂库版本锁定 5.9.4crowbar 的源码里支持正则表达式字面量比如/abc/这种写法但正则匹配引擎不是自己实现的而是链接 Oniguruma 这个正则库。源码包里不直接带 oniguruma 的编译产物需要你单独下载onig-5.9.4.tar.gz并编译安装。版本号被锁定在 5.9.4不是随便选一个新版就能保证行为一致因为旧版本导出符号和头文件布局在后续版本里变过。我一般建议按作者指定的版本来能少踩很多兼容坑。当年这个库托管在 geocities.jp 上那个站点已经关停现在找原版需要用镜像这也是为什么很多人在复现这套源码时会卡在依赖这一步。后续第 4 章我会专门讲它在 Linux 和 Windows 下分别怎么装。3. Windows 构建实战MinGW、GnuWin32、Cygwin 三选一怎么配3.1 先装 MinGW 工具链gcc、mingw32-make、bison、flex 四件套在 Windows 上编译这套源码最主流的组合是 MinGW GnuWin32 的 bison/flex。MinGW 提供 gcc 和 makeGnuWin32 提供 bison、flex、m4。装完第一件事不是急着 make而是确认四个命令都在 PATH 里:: 在 cmd 里逐个检查缺哪个补哪个 where gcc where mingw32-make where bison where flex :: 临时把工具目录加进 PATH当前窗口生效 set PATHD:\MinGW\bin;D:\GnuWin32\bin;%PATH%MinGW 安装时通过mingw-get-setup.exe拉组件我在 Windows 上一般勾 mingw32-base里面带着 gcc 核心和 mingw32-make。make 命令在 MinGW 里默认叫mingw32-make.exe不叫 make为了省事很多人会把它复制一份改名为make.exe放进 MinGW 的 bin 目录。GnuWin32 的 bison 和 flex 安装包建议选 “Complete package, except sources”只缺源码二进制的 exe 和辅助 DLL 都会装好。装完后where bison能看到路径才算合格。3.2 构建 crowbar_book_0_1从 make 到 crowbar.exe 的完整输出工具链就绪后进到源码目录执行构建我习惯用全路径调用 make避免 PATH 被别的环境变量污染cd C:\selflang\crowbar_book_0_1 D:\MinGW\bin\mingw32-make.exe如果一切正常你会看到 bison 先跑起来生成 y.tab.c 和 y.tab.h紧接着 flex 生成 lex.yy.c然后 gcc 挨个编译 main.c、interface.c、execute.c、eval.c、string.c 等文件。Makefile 里还带-Wall -Wswitch-enum -ansi -pedantic这些严格告警选项说明作者对自己代码的整洁度有要求。最终目录下出现 crowbar.exe构建完成。验证运行很简单crowbar.exe test\test.crb这一步容易翻车在换行符上如果 test.crb 是从 Linux 环境拷过来的Windows 下反而可能因为 LF 换行被词法分析器误解反之 Linux 读 Windows 的 CRLF 也会报 0x0d 错误这部分我放在第 5 章统一说。另外构建过程中如果提示cd ./memory; gmake.exe失败先检查 memory 子目录是否完整存在再确认你用的 make 能正确处理子目录递归调用。打包下载的源码偶尔会漏子目录这是第一个要排查的地方。3.3 三种 Windows 环境的取舍MinGW、GnuWin32、Cygwin 别混用Windows 下能跑这套源码的环境不止一个但各有脾气我整理成一张对比表环境方案生成 exe 是否依赖 DLLbison/flex 来源适合做的事MinGW GnuWin32不需要原生 Win32 exeGnuWin32 的 bison/flex产出可独立分发的 exeCygwin依赖 cygwin1.dllCygwin 包管理器里的 bison/m4/make模拟 Linux 的 configure 全流程WSL 或原生 Linux依赖 Linux 运行库apt 安装 bison/flex直接走 make.sh最省心三者混用是最常见的坑。比如你 MinGW 和 Cygwin 都装了在 cmd 里执行 make 时系统可能先命中 Cygwin 的 make导致链接时去找 Cygwin 的库最后生成一个需要 cygwin1.dll 才能跑的 exe。我一般一套环境只留一个 make用 MinGW 就只用mingw32-make.exe用 Cygwin 就在 Cygwin Terminal 里跑别在系统 PATH 里让两套工具打架。4. Linux 构建实战make.sh 编译 crowbar 和 oniguruma 的 configure 细节4.1 Linux 上一条路走通装 bison/flex跑 make.sh读 test.crbLinux 下比 Windows 省心不是一点点。Debian/Ubuntu 系先把依赖装齐再去编译# Debian/Ubuntu 系安装编译依赖 sudo apt install -y gcc make bison flex # 进入 crowbar 源码目录执行作者提供的构建脚本 cd crowbar_book_0_1 ./make.shmake.sh 本质是封装了 bison、flex、gcc 的完整调用序列避免不同机器上 Makefile 缺目标导致意外。脚本内部会先执行 bison 生成 y.tab.c 和 y.tab.h再执行 flex 生成 lex.yy.c随后按依赖顺序编译各模块。编译完成后用自带测试用例跑一遍# 运行解释器执行 test 目录下的脚本文件 ./crowbar test/test.crb如果在编译时遇到 bison 版本过新产生的告警通常不影响产物真正影响的是 test.crb 里的换行符后面第 5 章会专门讲。4.2 oniguruma 的 configure 流程Linux 三步安装Windows 要手动复制库文件oniguruma 是整个依赖链里最需要耐心的部分。它在 Linux 下走的是标准 autotools 流程# 解压后进入源码目录 tar -xvf onig-5.9.4.tar.gz cd onig-5.9.4 # configure 会检查 alloca、memcmp、可变长度原型等底层能力 ./configure make sudo make installconfigure 脚本会生成 config.h 和 Makefile并根据当前环境决定启用哪些底层实现。make install默认把头文件和静态库装到/usr/local/lib和/usr/local/includecrowbar 链接时用-lonig去找。Windows 下如果走 Cygwin 做同样操作make install很容易在.deps/euc_jp.Plo上失败或者在 MinGW 里遇到ranlib /usr/local/lib/libonig.a: No such file。这类问题的原因大多是 libtool 中途没生成最终归档文件解决方式也直接进入.libs目录手动执行一次ranlib libonig.a再把libonig.a复制到/usr/local/lib之后接着跑make install就能绕过。4.3 源码里的 hoge 和 foobar日文占位符不是 bug是习惯读这套源码的人注意力很容易被测试脚本里的 hoge、foobar 这种名字带走。我最初也以为是作者随手乱写后来查了资料才知道hoge 在日文编程社区里是通用的占位符地位相当于英文世界的 foo/bar。日本程序员写示例变量名时第一选择往往就是 hoge它没有任何业务含义就是“随便一个变量”。所以你在 test.crb 里看到print(hoge)或者变量名 v1、n1 混着出现不用怀疑源码有问题这是作者的习惯。真正值得研究的是表达式怎么解析、变量表怎么管理而不是名字本身。5. 避坑五条最高频编译报错的现象、原因和解决记录5.1 bison 不在 PATHprocess_begin 报错把整个 make 停在这一步现象在 Windows 下执行mingw32-make立刻输出类似这样的错误后中止bison --yacc -dv crowbar.y process_begin: CreateProcess(NULL, bison --yacc -dv crowbar.y, ...) failed. make (e2): 系统找不到指定的文件。原因make 在执行 Makefile 里的 bison 规则时在 PATH 里找不到 bison.exe。GnuWin32 没装或者装了但 bin 目录没加进 PATH都会引发这个结果。解决先where bison确认如果确实缺失把 GnuWin32 的 bin 目录加进 PATH 后重跑。若安装目录在D:\GnuWin32\bin命令是set PATHD:\GnuWin32\bin;%PATH%。改完环境变量后要重新打开 cmd 再执行 make否则仍会沿用旧环境。5.2 GnuWin32 装进带空格路径m4 把 Program Files 拆成两个参数现象bison 本身能启动但紧接着 m4 报一串离奇错误m4: cannot open Files: No such file or directory m4: cannot open (x86)\GnuWin32\Bison/share/bison: No such file or directory原因GnuWin32 被默认安装到了类似D:\Program Files (x86)\GnuWin32的路径。bison 调用 m4 加载宏文件时带空格的完整路径被拆成了多个参数m4 拿Files当文件名去找。解决卸载后重新安装到无空格路径比如D:\GnuWin32。这是最省事的方案优先级高过手动加引号改配置。以后再遇到 m4 打不开文件第一反应就查安装路径里有没有空格和中文字符。5.3 y.tab.h 没生成flex 找不到头文件gcc 跟着翻车现象编译 mycalc 或 crowbar 时先出现mycalc.l:3:19: error: y.tab.h: No such file or directory gcc: y.tab.c: No such file or directory原因.y 语法文件和 .l 词法文件的生成顺序错了。y.tab.h 必须由 bison 先处理 .y 文件产生flex 处理 .l 文件时才能通过#include y.tab.h拿到 token 定义。如果 Makefile 的依赖关系不完整或者你手动执行时只跑了 flex 没跑 bison就会翻车。解决手动按顺序执行 bison、flex、gccbison --yacc -dv mycalc.y flex mycalc.l gcc -o mycalc.exe y.tab.c lex.yy.c如果用了这条顺序仍然失败回头检查 bison 是否成功生成了 y.tab.h而不是直接怀疑编译器。5.4 Windows 行尾进入 Linuxcrowbar 报 0x0d 语法错误现象Linux 下编译成功后运行./crowbar test/test.crb报错信息像这样5: 语法错误(0x0d)原因test.crb 文件是从 Windows 环境复制过来的行尾是 CRLF。crowbar 的词法分析器把每行结尾的\r当成一个非法字符于是在第 5 行附近报 0x0d 错误。这个问题和编译器无关纯粹是文件格式。解决把 .crb 文件的换行符转成 Unix 风格# 单个文件转换 dos2unix test/test.crb # 或者目录内批量处理 find test -name *.crb -exec sed -i s/\r$// {} \;我一般会养成习惯从 Windows 解压源码包后先全目录批量清理一遍行尾再开始编译。这不仅针对 crowbar任何跨平台源码包都适用。5.5 链接期缺库-lmsvcp60 和 libonig.a 的经典错位现象在 Linux 上编译 crowbar_book_0_4链接器报/usr/bin/ld: cannot find -lmsvcp60同时在 Windows 的 MinGW 环境里编译 oniguruma 后执行make install又报ranlib /usr/local/lib/libonig.a: No such file原因-lmsvcp60是 MSVC 的运行库参数是源码里某个 Windows 专用 Makefile 或链接配置残留下来的Linux 上根本没有这个库文件。而 MinGW 的make install失败是因为 oniguruma 用 Cygwin 风格的 libtool 生成归档文件路径和 MinGW 预期不一致ranlib 找不到目标。解决Linux 上编辑对应的 Makefile把-lmsvcp60从链接行去掉然后重新编译。MinGW 环境则进入 onig-5.9.4 源码目录手动补齐cd .libs ranlib libonig.a cp libonig.a /usr/local/lib/ cp ../oniguruma.h /usr/local/include/把静态库和头文件放到位后再回到 crowbar 目录执行 make链接就能找到 libonig 了。这个坑的教训是旧日文源码包里的链接参数经常带有原作者 Windows 开发环境的残留编译失败时先看清楚 ld 在找哪个库再决定是删参数还是补库文件。6. 拿到源码包先做验证make clean 与最小化编译链路6.1 用 make clean 强制走一遍完整生成链路网上很多源码包为了省事打包时直接塞入了编译产物比如 y.tab.c、lex.yy.c 甚至现成的 exe。你拿到的代码里这些文件都在运行起来看似正常但一旦换个平台或改一行语法整个体系就露馅。我的习惯是拿到任何语言实现源码包第一件事先跑make clean把生成物全清理掉只留手写源文件然后重新构建一遍。以 crowbar 为例cd crowbar_book_0_1 make clean make如果make clean后重新 make 依然能顺利产出 crowbar说明这个包确实是源码级完整而不是靠预生成文件撑场面的半残包。这招对判断资源完整度很管用因为很多下载包里 y.tab.c 还在但原始的 crowbar.y 已经被删掉或损坏直接 make 不会报错改一下语法文件才露馅。6.2 用 calc 目录做工具链探针验证完整工具链不需要一上来就挑战 crowbar。calc 是最简单的一层先在这里确认 gcc 和 make 没问题。进去看一眼 Makefile 内容确认没有额外依赖直接执行构建再手动输入表达式测试输出。这个小动作花两分钟能把环境问题隔离在最小范围内。我也常用它判断当前机器的基础编译器是否正常避免一上来就在复杂项目里排查“到底是我环境坏了还是源码坏了”。整体链路验证通过后再进入 mycalc 接触 yacc/lex最后才是 crowbar 和 Diksam。从那以后我每次拿到这类源码包都强制自己先make clean再全量重编用最小例子探明工具链再往深了改代码。这个习惯帮我把“环境问题和源码问题”分开省下过不少白费功夫的排查时间。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →