一个简单的计算器的编译原理
约 739 字大约 2 分钟
2025-12-08
回顾一下之前我们对一个程序的分解。我们把没有经过加工的程序叫做源代码。源代码中必须包含合法的字符组合也就是符号 (token),否则程序就会报错。同理,一串符号也必须符合一定的语法 (syntax) 规则,否则也会报错。经过语法分析,我们得到抽象语法树 (AST)。AST 的全称是 Abstract Syntax Tree。它是树这种数据结构在实际编程的应用。得到抽象语法树之后对于我们这个计算器项目来说已经足够了,直接对各种运算进行求值就可以得到结果了。如下图所示,整个编译执行的过程以及输入输出的关系。
在此我们先略过细节,但是学习一下黑话还是很有必要的
- 源代码 source code:没有经过加工的程序,包含合法的字符组合。
- 词法分析 lexical analysis:将源代码转换为符号序列的过程,做这件事的程序叫做词法分析器 (lexer)。
- 符号 token:源代码中的合法字符组合,词法分析器扫描源代码时,按顺序产生 token 序列。
- 语法分析 syntax analysis:将符号序列转换为抽象语法树的过程,做这件事的程序叫做语法分析器 (parser)。
- 抽象语法树 AST:符合语法规则的符号组合,由语法分析器读取 token 序列根据语法规则生成。
- 求值 evaluation:对抽象语法树进行计算的过程,也被叫做求值器 (evaluator)。
现在,我们已经把这个这个简单的计算器分解为了多个部分。每个部分都有自己的职责,也有自己的输入输出。我们可以把这些部分组合起来,就可以实现一个简单的计算器。既然是多个部分,就意味着每个部分都可以是一个独立的模块。现实中怎样将项目划分为合理的模块是一个非常复杂的问题。但像对于我们这个项目来说,有许多现成的经验可以借鉴。
我们可以把模块分为两类:数据模块和功能模块。数据模块负责存储和管理数据,而功能模块负责做具体的动作。于是我们有了以下的模块:
- token
- ast
- lexer
- parser
- evaluator
- repl
最后一个模块 REPL 不在我们的图中,但在我们最初提出的需求里。它的全称是 Read-Eval-Print-Loop。它是一个交互式的环境,用户可以在其中输入表达式,然后程序会对表达式进行求值,最后打印出结果。
事不宜迟,我们可以开始动手操作了。
