从零编写现代编译器 02 - 词法分析器:把文本切成 Token
上一篇里,编译器能处理的输入是硬编码的——它只认识 fn main() -> i64 { return 42; } 这一个程序,Token 序列直接写死在代码里。换一个数字就得改编译器源码。这不叫编译,这叫字符串替换。 这一篇要做的事情:给 Sprout 写一个词法分析器(lexer),让它读入任意源文本,逐字符扫描,输出一串带位置信息的 Token。后续的解析器将消费这个 Token 流,而不再关心原始字符。 词法分析器做什么 编译器前端的第一道工序是词法分析。它的输入是源文件的字节流,输出是 Token 序列。每个 Token 标记了一段源文本的语法角色:这是一个关键字,那是一个整数字面量,这里是左括号,那里是分号。 词法分析器不关心 Token 之间的组合是否合法。return return return 在词法层面完全没有问题——三个 Return Token。判断组合是否合法是解析器的工作。词法分析器只负责切割。 类比一下:词法分析像把一段中文拆成词语,每个词标上词性。"我吃了三碗饭"变成 [我/代词] [吃/动词] [了/助词] [三/数词...
从零编写现代编译器 01 - return 42:从源码到可执行文件
一个只返回 42 的函数,从源码文本变成本机可执行文件,中间到底经历了什么?这篇把整条路走一遍:手动分词、构造语法树、生成 LLVM IR、编译成目标文件、链接为二进制、运行并检查退出码。走完这条最短路径之后,后续每篇只需往流水线里加东西。 最小的程序 Sprout 语言的第一个合法程序只有一行有效逻辑: 123fn main() -> i64 { return 42;} fn 声明函数,main 是入口,-> i64 标注返回类型为 64 位有符号整数,函数体只做一件事——返回整数字面量 42。这个程序没有变量、没有条件、没有循环,但它足以驱动编译器的每一个阶段。 编译器要做的事情可以用一条流水线概括: 1源码 → 词法分析 → 语法分析 → 代码生成 → LLVM 工具链 → 可执行文件 下面逐段走完这条线。 源码到 Token:词法分析 词法分析器(lexer)的工作是把字符流切成有意义的最小单元——Token。对上面那段源码,手动分词的结果是: 1234567891011FN -- 关键字 fnIDENT(...
从零编写现代编译器 00 - 这门语言准备编译什么
多数编译器教程止步于计算器——解析 1 + 2 * 3,求值,结束。这个系列的终点不同:从一个空目录开始,逐步构建一个能检查类型错误、生成本机可执行文件、报告源码位置并接入编辑器的编译器。 目标语言暂名 Sprout,扩展名 .spr,编译器命令暂定 sproutc。实现语言是 Rust,主后端是 LLVM。进阶部分还会接入 Cranelift 即时编译(Just-In-Time compilation, JIT)、WebAssembly 组件和 MLIR 张量编译。 这篇导读划定语言范围、列出编译架构、给出语义边界草案并确认先修条件。代码从第 01 篇开始;本篇没有可运行的编译器。 本篇目标 读完这篇,需要能回答一个问题:给定一段 Sprout 源码 fn main() -> i64 { return 42; },区分源语言、实现语言、中间表示和机器码四个层次。 这个区分贯穿整个系列。源语言是读者设计的 Sprout,实现语言是 Rust,中间表示包括 AST、HIR、SSA 和 LLVM IR,机器码是目标架构的二进制指令。混淆任何两层都会在后续调试中制造困惑。 最终...
现代编译器写作计划:从 Rust 到 LLVM、Wasm 和 MLIR
这不是一篇“把一个表达式计算器写出来”的短教程,而是一套可以拆成连载的编译器写作计划。目标有三个:读者能跟着写,例子能跟着跑,技术选型不会很快过时。技术锚点按 2026-09-05 的官方文档来定:Rust 1.98.1 / Rust 2024 edition,LLVM 23.1.0,Wasmtime 45.0.0,WASI 0.2/0.3 preview,以及仍在活跃演进中的 MLIR。 这本书要写什么 这套教程的主线不是“如何写一个最小可运行编译器”,而是“如何写一条今天仍然成立的编译器路线”。前半部分讲前端:词法、语法、名字绑定、类型检查、错误恢复。中间部分讲 IR:AST、typed HIR、SSA、控制流图、数据流分析。后半部分讲后端:LLVM IR、Cranelift、WebAssembly component model、WASI,再加一章 MLIR 作为多层 IR 的扩展方向。 这条路线的好处很直接。读者先学到经典编译器骨架,再看到现代工具链怎么把这条骨架接到真实系统上。写完以后,代码可以落到原生二进制,也可以落到 Wasm component;同一套中端还能顺着...
分布式 ID 系统设计:从需求约束推导架构
数据库自增列足以支撑单库业务。系统拆成多个写入节点后,ID 生成突然变成一项基础设施能力:它要避免碰撞,不能拖慢写路径,还要经受扩容、时钟异常、数据库切换、配置漂移和跨地域网络分区。 这类系统最容易从算法名称开始讨论:UUID、Snowflake、数据库序列、号段。算法当然重要,但架构并不是从算法列表里挑出来的。它来自一组可验证的约束:唯一到什么范围,顺序要强到什么程度,故障时允许停多久,ID 可以暴露什么信息,以及团队能维护多少协调状态。 先定义 ID 的合同 “生成一个全局唯一且递增的 ID”看似明确,实际混合了几种不同语义。若不拆开,评审阶段达成的共识很可能只是每个人对同一句话的不同理解。 唯一性有作用域 唯一性至少要说明三个边界: 命名空间:全公司、单业务、单租户、单表,还是单个分片。 时间范围:进程存活期间、数据保留期,还是系统整个生命周期。 保证方式:确定性不重叠,还是碰撞概率低到工程上可接受。 数据库序列和合理分配的 Snowflake 节点空间可以给出确定性的不重叠保证。UUIDv4 依靠足够大的随机空间降低碰撞概率。两者都常被称为“全局唯一”,但证明路径并...
tmux 教程:把一次 SSH 登录变成可恢复的终端工作台
终端窗口关闭、SSH 连接抖动或笔记本合盖,常常会把正在运行的构建、日志观察和远程编辑一起打断。tmux 的价值不在于把屏幕切成几块,而在于把程序运行的归属从一次 terminal client 连接中分离出来:client 可以离开,session 仍留在主机上。 tmux 是 terminal multiplexer,不是终端模拟器,也不是进程监督器。它运行在已有终端里,管理一组带 pseudo-terminal 的程序;它能抵抗 terminal 或网络连接中断,却不会让程序跨主机重启自动恢复。这个边界决定了 tmux 适合管理交互式工作台,不应被当成 systemd、容器编排器或备份系统的替代品。 先建立正确的对象关系 tmux server 管理 client、session、window 和 pane。client attach 到一个 session;session 维护 window 列表;window 由一个或多个 pane 组成;每个 pane 都承载一个 pseudo-terminal。window 还可以链接给多个 session,因此这不是一棵严格的 ...
深入 HBase 19 - HBase 3.0 与设计边界
HBase 3.0.0 是大版本升级,不应被写成 2.6 的小补丁。官方下载页显示 3.0.0 发布于 2026-08-05,同时 2.6.6 发布于 2026-06-09,2.5.15 被标为 stable release。基础机制仍可用 2.6.6 解释,但 3.0 的部署、客户端连接和 API 兼容边界必须单独处理。 本篇只抓一个核心问题:3.0 相对 2.6 改了什么;哪些工作负载应该选别的数据库。 版本位置图 flowchart TD A[HBase 2.6.6 基础机制] --> B[row key / Region / WAL / MemStore / HFile] B --> C[2.x 客户端与运维习惯] C --> D[HBase 3.0.0 大版本升级] D --> E[JDK 17+] D --> F[Hadoop 2 移除] D --> G[MasterRegistry/RPC bootstrap 默认路径] D --> H[deprecated/private API 清理] H...
深入 HBase 18 - Metrics、hbtop、Compaction 与性能调优
HBase 性能问题很少只由一个参数造成。热点 Region、StoreFile 过多、compaction backlog、BlockCache 命中率下降、GC 停顿、HDFS locality 变差,都会表现成读写延迟上升。调优的第一步不是改配置,而是把症状映射到可观察对象。 本篇只抓一个核心问题:如何用 Metrics、hbtop、JMX 和服务端状态识别热点 Region、compaction backlog、缓存失配、GC 和磁盘瓶颈。 观察路径图 flowchart TD Slow[读写变慢] --> Client[Client retry/timeout] Slow --> RS[RegionServer metrics] RS --> Hot[Region #REQ/S/#WRITE/S] RS --> Cache[BlockCache hit/miss] RS --> Store[#SF / StoreFile size] RS --> Compaction[Compaction queue/prog...
深入 HBase 17 - Kerberos、RPC 保护与 ACL
HBase 安全不是一个开关。Kerberos 解决身份认证,RPC protection 解决传输层保护,ACL 解决 HBase 资源授权,HDFS 和 ZooKeeper 还要各自按安全模式配置。任何一层缺失,生产集群都会留下绕过路径。 本篇只抓一个核心问题:客户端身份、服务认证、传输保护和表权限如何衔接。 安全路径图 flowchart TD Client[Client principal] --> KDC[Kerberos KDC] RS[RegionServer principal] --> KDC Master[HMaster principal] --> KDC Client --> RPC[HBase RPC/SASL] RPC --> AuthN[authentication] AuthN --> Protection[hbase.rpc.protection] Protection --> ACL[AccessController ACL] ACL --> Region[Regi...
深入 HBase 16 - Coprocessor、Endpoint 与 Phoenix 边界
Coprocessor 把一部分逻辑放进 HBase 服务端进程执行。这个能力很强,也很危险:代码和 RegionServer 在同一个 JVM、同一套权限、同一条请求路径里运行。Endpoint 能做局部聚合和自定义 RPC,但它不是分布式 SQL 引擎;Phoenix 提供 SQL 层,但 Phoenix 和 HBase 的升级边界也不能混写。 本篇只抓一个核心问题:计算下推能做什么,何时会破坏隔离、升级和运维边界。 Coprocessor 路径图 flowchart TD Client[HBase Client] --> RPC[RegionServer RPC] RPC --> Observer[Observer hooks] Observer --> Region[Region] Region --> Store[Store/MemStore/HFile] Client --> Endpoint[Endpoint service] Endpoint --> Region Region --> WAL[...





