ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

purescript-native编译器原理(一):PureScript源码到corefn JSON再到IL中间语言的完整旅程

purescript-native编译器原理(一):PureScript源码到corefn JSON再到IL中间语言的完整旅程 purescript-native编译器原理(一):PureScript源码到corefn JSON再到IL中间语言的完整旅程【免费下载链接】purescript-nativeA native compiler backend for PureScript (via C or Golang)项目地址: https://gitcode.com/gh_mirrors/pu/purescript-nativepurescript-native是一个PureScript 原生编译器后端它把 PureScript 编译出的 corefn JSON 进一步翻译成 C或 Go源码最终生成可调试、可移植的本地原生可执行文件。本文带你完整走一遍这条编译旅程——从 PureScript 源码出发经过corefn JSON 中间表示进入pscpp自研的IL中间语言再经多轮优化最终打印成 C 代码。一、为什么需要 PureScript 原生编译器PureScript 是类型安全的函数式语言官方默认目标是 JavaScript。但如果你想生成真正的本地二进制没有 JS 运行时开销用 C 的std::shared_ptr做自动内存管理拿到接近 C 的性能直接调用 C/C 库FFI 就是 C 函数生成健全、可调试的代码——每行都能对应回 PureScript 源码结构那么purescript-native就是答案。它的pscpp工具只依赖 C11 标准库无额外运行时。类型映射非常直接见 README-cpp.mdPureScript 类型C 实现IntintNumberdoubleBooleanboolString/Charstd::stringUTF-8Arraystd::vectorboxed对象 / 泛型值boxed智能指针 联合体二、编译管线总览五步走整条旅程可以概括为五步后三步全部发生在pscpp内部PureScript 源码 │ purs compile ▼ corefn JSON每个模块一个 .json 文件 │ pscppHaskell 编写的转译器 ▼ CoreFn AST ──► IL命令式中间语言──► 优化器多轮 Pass ──► C .h/.cpp 源码 │ makeC 编译器 ▼ 本地原生可执行文件入口逻辑在 app/Main.hspscpp接收一批 corefn JSON 文件先向输出目录写入运行时支撑文件writeRuntimeFiles 会输出runtime/下的 purescript.h、purescript.cpp 等 5 个文件再用Par.mapM并行转译每个模块。三、第一步purs 生成 corefn JSONcorefnCoreFn是 PureScript 官方定义的纯函数式中间表示经类型检查与类型类消解后产生并用 JSON 序列化——这正是pscpp的输入。这意味着purescript-native与 PureScript 编译器松耦合任何能产出 corefn 的工具链都可用JSON 形式便于检查、缓存pscpp会对比 JSON 文件与输出头文件的时间戳跳过未变更模块见 generateCode。四、第二步JSON 反序列化为 CoreFn 模块pscpp用 Aeson 把 JSON 解析为ValueparseJson再借助 PureScript 库自带的moduleFromJSON还原成带注释信息的Module AnnjsonToModule。这一步是纯数据还原真正的翻译从下面开始。五、第三步CoreFn → IL表达式变命令式IL 直接复用 PureScript 的CoreImp AST命令式 AST核心转换函数是 moduleToIL。它做的事情模块名去冲突renameImports给导入模块生成别名避免与声明名撞车src/CodeGen/IL.hs逐个声明转换bindToIL→ valueToIL 是翻译的核心逐条处理 CoreFn 表达式字面量 → IL 字面量LambdaAbs→ ILFunctionlambda 提升为一等值App应用 → 嵌套App节点模式匹配Case→ 一串IfElse分支bindersToIL构造子模式先判断InstanceOf标签再逐字段取属性代数数据构造子→ 返回对象字面量的函数对象里带一个类型标签字段如{Just: true, ...}newtype自动拆箱直接返回内部值所有值统一包成boxed类型——它在运行时 runtime/purescript.h 中是std::shared_ptrvoid加一个存放int/double/bool的联合体既支持引用计数又避免小值堆分配。命名规则由 src/CodeGen/IL/Common.hs 保证合法C 关键字会被加下划线后缀properToIL模块名中.替换为_运行时保留名box、unbox、dict等也不能占用ilLiterals。六、第四步优化器 Pass让 IL 又快又省未优化的 IL 只是能跑Optimizer.hs 中的 optimize 会按固定顺序跑多轮 Pass每轮用 untilFixedPoint 迭代到不动点Pass作用源码位置内联unsafeCoerce/unsafePartial消除零成本安全操作Inliner.hsmagicDo把Effect/ST/Eff的、pure、discard展开成顺序语句do 块变直线代码MagicDo.hsTCO 尾调用优化尾递归函数改写成while循环_tco_loop_/_tco_done_递归永不爆栈TCO.hs内联公共值/运算符识别类型类字典把Data.Semiring.semiringInt.add 1 2还原成原生1 2、、字符串拼接、位运算同理Inliner.hstidyUp折叠嵌套块/If、去尾部代码、eta 化、IIFE 求值、变量内联Optimizer.hs删除未用结果、合并变量声明清理垃圾语句Optimizer.hs正是这些 Pass 让最终的 C 既贴近手写风格、又可直接用 GDB 单步调试。七、第五步Printer 打印 C 源码Printer.hs 把优化后的 IL 漂亮地打印成 CFunction节点变成 lambda静态值提升为auto f() - const boxed {...}惰性初始化、IfElse/While/For直接对应 C 控制流、Indexer变成::()或[]访问Printer.hs。每个模块最终产出两个文件transpilesrc/Module.h—— 接口声明供其他模块#includesrc/Module.cpp—— 实现 模块头部的#include 尾部的 foreign 导入声明。若你的编译器不支持 Unicode 字面量如 gcc加--ucns选项会把非 ASCII 字符转为\UXXXXXXXX编码toUCN。八、关键源码地图 模块职责app/Main.hs命令行入口、JSON 读取、输出文件管理src/CodeGen/IL.hsCoreFn → IL 翻译核心src/CodeGen/IL/Common.hs标识符合法化、内置名表src/CodeGen/IL/Optimizer.hs优化 Pass 编排src/CodeGen/IL/Optimizer/TCO.hs尾递归 → 循环src/CodeGen/IL/Optimizer/MagicDo.hsdo 块脱糖src/CodeGen/IL/Optimizer/Inliner.hs类型类字典内联为原生运算符src/CodeGen/IL/Printer.hsIL → C 源码runtime/purescript.hboxed等运行时核心类型support/Makefile--makefile生成的构建模板九、如何上手体验安装 PureScript0.13与 C11 工具链clang 或 gccpscpp --makefile生成 GNU Makefile源码放src/C FFI 放ffi/然后make debug或make release多核可加-jN。细节可参阅 README-cpp.md 的 Getting Started 章节。十、小结这条PureScript 源码 → corefn JSON → IL → 优化 → C → 二进制的旅程体现了原生编译器后端设计的精髓用官方 JSON 中间表示解耦前端用命令式 IL 多轮 Pass 控制生成代码质量。理解了moduleToIL、optimize、prettyPrintIL这三个函数你就掌握了pscpp的全部骨架。下一期我们将深入boxed类型系统与 C FFI 的协作机制敬请期待【免费下载链接】purescript-nativeA native compiler backend for PureScript (via C or Golang)项目地址: https://gitcode.com/gh_mirrors/pu/purescript-native创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表