“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。
md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。
要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。
在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。
要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。
工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。
写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。
适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。
你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。
全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。
这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。
不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。
产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。
如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。
编译器与语言设计入门:从语法到代码生成的全景指南
一篇系统介绍编译器核心原理与语言设计实践的深度教程,涵盖词法分析、语法分析、语义分析、中间代码生成与优化等关键环节。
引言
编译器是计算机科学中最为经典且深具影响力的系统软件之一。它将人类可读的高级语言代码转换为机器可执行的指令,是编程语言与硬件之间的桥梁。本笔记基于 Douglas Thain 的《Introduction to Compilers and Language Design》(2021版)一书,从实践与理论结合的角度,深入拆解编译器的各个阶段,帮助读者建立完整的编译器知识体系。
编译器的整体架构
一个典型的编译器可分为前端(Front End)和后端(Back End)。前端负责分析与理解源代码,后端负责生成目标代码。具体包括以下阶段:
- 词法分析(Lexical Analysis):将源代码的字符流转换为有意义的词素(Token)。
- 语法分析(Syntax Analysis):根据语法规则将词素序列解析为抽象语法树(AST)。
- 语义分析(Semantic Analysis):检查语法树的语义正确性,如类型检查、作用域解析。
- 中间代码生成(Intermediate Code Generation):生成与机器无关的中间表示(IR)。
- 代码优化(Code Optimization):对中间代码进行变换,提高执行效率或减少资源占用。
- 目标代码生成(Code Generation):将优化后的中间代码转换为目标机器的汇编或机器码。
词法分析:从字符到Token
词法分析器(Lexer)通常使用有限自动机(Finite Automaton)实现。它读取源代码,根据预定义的正则表达式模式识别关键字、标识符、运算符、字面量等。例如,对于C语言中的 int a = 10;,词法分析器会输出:KEYWORD:int, IDENTIFIER:a, OPERATOR:==, INTEGER_LITERAL:10, SEMICOLON。
该阶段不关心语法结构,只关心“是什么”。实践中,词法分析器通常由工具(如 Lex、Flex)自动生成,但手工编写递归下降的词法分析器也常见于教学编译器。
语法分析:从Token到语法树
语法分析器(Parser)接收Token流,根据语言的上下文无关文法(Context-Free Grammar)构建抽象语法树(AST)。常用方法有两种:
- 自顶向下分析(Top-Down):从起始符号开始,试图匹配输入。典型实现是递归下降解析器(Recursive Descent Parser),它直接对应文法产生式,易于手工编写,但需要处理左递归问题。
- 自底向上分析(Bottom-Up):从输入Token开始,逐步归约到起始符号。典型实现是LR解析器(如Yacc、Bison),能够处理更广泛的文法,但生成过程复杂。
例如,对表达式 3 + 4 * 5,语法分析器会生成如下的AST:
+
/
3 *
/
4 5
该树体现了运算符优先级:乘法先于加法执行。
语义分析:赋予代码意义
语法分析只保证结构正确,但无法判断 int x = "hello"; 是否合法。语义分析阶段进行类型检查、作用域解析、变量声明验证等。它遍历AST,维护符号表(Symbol Table),记录每个标识符的类型、作用域和内存位置。
例如,在静态类型语言中,语义分析会确保:
- 赋值操作中左右类型兼容
- 函数调用时参数个数与类型匹配
- 变量在使用前已被声明
如果发现类型错误(如将字符串赋给整数变量),编译器会报告语义错误。
中间代码生成:抽象化的桥梁
中间代码(Intermediate Representation, IR)是独立于源语言和目标机器的抽象指令形式。常见格式包括:
- 三地址码(Three-Address Code):每条指令最多包含三个操作数,如
t1 = a + b - 静态单赋值形式(SSA):每个变量只被赋值一次,便于优化
- 栈式虚拟机指令:如JVM字节码
中间代码的设计目标是:既保留足够的语义信息供优化,又足够简单以支持多种后端目标。
代码优化:让程序跑得更快
优化器在不改变程序语义的前提下,对IR进行变换。常见的优化技术包括:
- 常量折叠(Constant Folding):在编译时计算常量表达式,如
2 + 3直接变为5 - 死代码消除(Dead Code Elimination):移除永远不会执行的代码
- 循环优化(Loop Optimization):如循环不变式外提(Loop Invariant Code Motion)、循环展开(Loop Unrolling)
- 寄存器分配(Register Allocation):将频繁使用的变量分配到CPU寄存器中,减少内存访问
优化可以发生在多个层面:局部优化(基本块内)、全局优化(函数内)、过程间优化(跨函数)。
目标代码生成:最终产出
代码生成器将优化后的IR转换为目标机器的汇编或机器码。该阶段需要处理:
- 指令选择(Instruction Selection):根据IR操作选择合适的机器指令
- 寄存器分配(Register Allocation):使用图着色算法等策略分配物理寄存器
- 指令调度(Instruction Scheduling):重排指令顺序以利用CPU流水线
例如,将三地址码 t1 = a + b 转换为x86汇编:
mov eax, [a]
add eax, [b]
mov [t1], eax
语言设计考量
编译器设计不仅关乎实现,也与语言设计密切相关。书中强调了几个关键设计原则:
- 可读性与可写性:语言语法应易于人类阅读和编写,避免歧义
- 安全性:静态类型检查、内存安全机制(如Rust的所有权系统)
- 正交性:语言特性之间应尽量独立,避免意外的交互
- 可移植性:通过中间表示和抽象层,使同一语言能运行在不同平台上
例如,C语言的设计注重效率与底层控制,而Python则强调简洁与动态性,两者在编译器设计上差异巨大。
实践:构建一个微型编译器
书中附带了一个完整的教学编译器实现(C语言编写),支持简单的算术表达式和变量声明。读者可以跟随代码学习:
- 如何用递归下降解析器解析表达式
- 如何构建符号表并执行类型检查
- 如何生成三地址码并输出到文件
该实践项目是理解编译器内部机制的最佳入门途径。
总结
编译器不仅是一个翻译工具,更是计算机科学中算法、数据结构、形式语言与自动机理论的集大成者。通过系统学习编译器设计,你能深入理解编程语言的本质、程序执行的过程,以及如何构建高效、安全的软件系统。本书适合具备一定编程基础(如C语言)的读者,无需形式语言理论背景,即可从零开始掌握编译器核心知识。