尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

解释器模式前置:文法、BNF与AST

解释器模式前置:文法、BNF与AST
📅 发布时间:2026/7/21 23:30:51

本文是【GoF设计模式】系列第22篇的前置知识,更多内容欢迎关注公众号:咖啡八杯

image

前言

解释器模式是 GoF 23 种设计模式中公认比较难理解的一个。它的难不在代码本身,而在前置概念——文法和 AST(抽象语法树)。如果不先搞懂这两个概念,直接看解释器模式的代码会一头雾水。

本文从零开始,用数学表达式 3 + 4 * 2 作为贯穿全文的例子,把文法和 AST 讲清楚。

文法与 BNF

什么是文法

文法就是语法规则。先看一个自然语言的例子:

我  吃  苹果  ✅
我  苹果  吃  ❌

为什么第一句对、第二句错?因为汉语的规则是:

主语 + 谓语 + 宾语

这就是一条文法规则。虽然大部分人没刻意学过这条规则,但能用它判断一句话合不合法。

计算机也一样。要让计算机理解 3 + 4 * 2,也得告诉它规则:

一个表达式 → 一个数字,后面可以跟「加号 + 数字」或者「乘号 + 数字」

但上面这句话其实有歧义——"后面可以跟"到底能跟多少个?是先加还是先乘?计算机听不懂模糊的话。

BNF 符号速览

科学家发明了一套记号,叫 BNF(巴科斯范式),用来精确描述文法。BNF 总共就几个符号:

符号 意思 一句话解释
→ "由……组成" 左边是规则名,右边是它的结构
'...' 引号里的就是字面本身 '+' 就是加号字符
| 或者 '+' | '*' 要么是加号要么是乘号
(...) 分组 把一堆东西捆在一起
(...)* 重复 0 次或无数次 可以有,也可以没有
普通单词 引用另一条规则 跳到那条规则去看

就这 6 个符号,没了。

用 BNF 描述数学表达式

数学表达式 3 + 4 * 2 和 10 * 2 + 3 * 5 的文法用 BNF 写出来是这样的:

expr   → term ('+' term)*
term   → factor ('*' factor)*
factor → NUMBER

逐句翻译:

第一句:expr → term ('+' term)*

expr         →      term          ('+' term)*│                    │                   ││                    │                   └── 后面可以跟 0 组或多组「+ 号 + 另一个 term」│                    ││                    └── 开头先是一个 term│└── 「一个 expr(表达式)由以下部分组成:」

连起来:一个表达式 = 一个 term,后面可以跟零组或多组「加号 + 又一个 term」。

举个例子:

  • term → 就是一个 term(没有加法)
  • term + term → 两个 term 相加
  • term + term + term → 三个 term 相加

第二句:term → factor ('*' factor)*

同理:一个 term = 一个 factor,后面可以跟零组或多组「乘号 + 又一个 factor」。

第三句:factor → NUMBER

一个 factor 就是一个数字。

注意:term、factor 这些名字不是关键字,可以随便起名,比如:

加法式 → 乘项 ('+' 乘项)*
乘项   → 因子 ('*' 因子)*
因子   → 数字

意思完全一样。

推导一个实际表达式

光看规则还是抽象。拿 3 + 4 * 2 实际走一遍推导过程:

第1步:  expr                                ← 从根规则开始
第2步:  → term ('+' term)*                 ← 展开 expr
第3步:  → term '+' term                    ← 遇到 '+', 展开一组
第4步:  → factor '+' term                  ← 第一个 term 展开成 factor
第5步:  → "3" '+' term                     ← factor 匹配到数字 3
第6步:  → "3" '+' factor '*' factor        ← 第二个 term 展开成 factor * factor
第7步:  → "3" '+' "4" '*' "2"              ← 两个 factor 分别匹配到 4 和 2

推导完毕,所有字符都匹配上了 → 3 + 4 * 2 是合法的。

为什么文法层级决定优先级

注意第 6 步:term 展开成了 factor '*' factor,也就是说乘法在 term 这一层就被消化掉了,根本没机会传到 expr 层。

expr   → term ('+' term)*    ← expr 只看得见加法
term   → factor ('*' factor)*  ← term 内部处理乘法

所以 3 + 4 * 2 在计算机眼里是 3 + (4 * 2),不是 (3 + 4) * 2。优先级靠层级结构而非约定决定——在文法中嵌套的层级越深,优先级越高。这是 BNF 最核心的价值。

AST(抽象语法树)

从推导过程长出一棵树

上一节的推导过程,每一步都在"展开"规则。如果把展开的过程画出来,会看到一棵树在慢慢长成:

第1步:  expr第3步:    expr/    \term  term第7步(最终):expr/     \term     term│       /    \factor  factor factor│      │      │3      4      2

这棵树叫语法树——它完整记录了 3 + 4 * 2 是怎么从文法规则推导出来的。

从语法树到 AST

仔细看上面的树,里面有 expr、term、factor 这些节点。这些是推导过程中的中间产物,不是表达式里真正的东西。

问一个人 3 + 4 * 2 的运算结构,他会说:"就是一个加法,左边是 3,右边是 4 乘 2。"他不会说:"这是一个 expr,它展开成一个 term 加一个 term……"

所以需要抽象语法树(AST)——把那些中间推导节点去掉,只保留真正有意义的东西:

          AddExpr/       \Number(3)    MulExpr/       \Number(4)  Number(2)

AST 为什么叫"抽象"? 因为它丢弃了:

  • 空格——解析完就没用了
  • expr、term 等中间概念——只是推导过程的脚手架
  • 括号(如果有的话)——优先级已经被树的结构固定了

只留下真正有用的信息:要算什么运算、操作数是谁。

对比三种表现形式:

形式 长什么样 特点
字符串 "3 + 4 * 2" 人类写的原始输入,含空格
语法树 包含 expr、term 等中间节点 信息完整但啰嗦
AST 只有 Add、Mul、Number 只保留运算结构,干净

AST 的两类节点

看这棵树:

          AddExpr           ← 非终结符(内部节点)/       \Number(3)    MulExpr       ← 非终结符(内部节点)/       \Number(4)  Number(2) ← 终结符(叶子节点)
节点类型 对应文法中的 说明
叶子节点(终结符) factor → NUMBER 直接有值,不用问别人,像一个士兵——自己就有战斗力
内部节点(非终结符) expr → ...、term → ... 需要先问下属拿到结果,才能决策,像一个将军

AST 的执行顺序

AST 的执行顺序是从下往上、从左到右(后序遍历):

          AddExpr            第3步: 3 + 8 = 11/       \/         \Number(3)     MulExpr       第2步: 4 * 2 = 8↑          /     \│         /       \第1步: 3  Number(4)  Number(2)↑          ↑第1步: 4    第1步: 2

执行顺序:

  1. 先算叶子节点:3、4、2(直接就是自身值)
  2. 再算 MulExpr:4 * 2 = 8
  3. 最后算 AddExpr:3 + 8 = 11

这种"先子节点、后父节点"的顺序天然适合递归。每个节点先问完孩子,再算自己:

最外层节点(AddExpr):"告诉我值是多少?"它问左边 → 打开是 3它问右边 → 又是个套娃(MulExpr)打开 → 左边是 4,右边是 2"4 * 2 = 8"好,现在知道了:3 + 8 = 11

每一层只管一件事:问左边要结果,问右边要结果,然后自己算。这就是递归求值的本质,也是解释器模式中 interpret() 方法的底层逻辑。

总结

概念 一句话记住
文法 描述"一句话该怎么写"的规则
BNF 精确描述文法的记号,总共就 6 个符号
推导 按规则一步步展开,直到匹配所有字符
优先级 在文法中嵌套的层级越深,优先级越高
语法树 文法推导过程的完整记录
AST 去掉中间产物,只保留运算结构的精简树
叶子节点 直接有值,不用问别人
内部节点 需要先问子节点,再算自己
执行顺序 从下往上(后序遍历),每个节点递归计算

理解文法和 AST 之后,再看解释器模式就简单了:每条文法规则对应一个类,按文法构建 AST,递归执行 interpret()。把文法、AST 和递归求值写成代码,就是解释器模式。

技术交流 & 更多原创内容,关注公众号:咖啡八杯

相关新闻

  • 2026绍兴汽车贴膜门店实测测评:5家正规门店横向对比,星空汽车贴膜综合领先 - 米諾
  • 自主AI智能体开发指南:从原理到实践
  • 如何彻底解决腾讯游戏ACE-Guard卡顿:免费开源性能优化工具完全指南

最新新闻

  • 工业时序数据融合:从“看不懂“到“读得懂“的技术突围
  • Kafka消费者核心机制与生产环境优化实践
  • 安徽废气处理厂家推荐/废气治理厂家哪家好?2026避坑指南:4个坑+5条硬标准,教你选对靠谱商家 - mobible
  • 抖店无货源一件代发售后自动化|抖掌柜自动售后设置,一键处理退款退货工单 - 抖掌柜
  • Kafka 3.1.0单机与集群环境搭建指南
  • 大模型API调用中的Token优化:从原理到工程实践的成本控制方案

日新闻

  • AI云原生实战05-金融AI上云最难的不是技术,是“不出事“——TCE银行风控架构拆解
  • 2026年GEOSEO优化公司选型深度测评:五大硬核标准严选,这六家重塑搜索增长新格局 - 品牌前沿专家
  • **核验!2026年7月卡地亚香港**售后网点地址及服务电话公告 - 卡地亚服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号