惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
WordPress大学
WordPress大学
Vercel News
Vercel News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
L
LangChain Blog
雷峰网
雷峰网
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
NISL@THU
NISL@THU
Scott Helme
Scott Helme
量子位
S
Security Affairs
T
Threat Research - Cisco Blogs
博客园_首页
云风的 BLOG
云风的 BLOG
D
Docker
AWS News Blog
AWS News Blog
腾讯CDC
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
U
Unit 42
Recent Announcements
Recent Announcements
Apple Machine Learning Research
Apple Machine Learning Research
G
Google Developers Blog
T
The Exploit Database - CXSecurity.com
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 热门话题
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Last Watchdog
The Last Watchdog
C
Cybersecurity and Infrastructure Security Agency CISA
IT之家
IT之家
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
F
Full Disclosure
L
Lohrmann on Cybersecurity
The Hacker News
The Hacker News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
S
Security @ Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Check Point Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
I
InfoQ

博客园 - daviyoung

Windows 11 22H2 安装 .NET Framework 3.5 完整教程 System.Threading.Timer 详细讲解 Agent 开发入门(一):从零构建你的第一个智能体 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(五)表达式求值 python使用plotly绘制图表 手把手搭建OPC UA服务器 图像处理库Pillow的使用:批量裁剪图片 python-docx库的使用:图片插入到word文档里 modbus(二)用NModbus4库实现Modbus tcp从站 Jenkins 容器化实践:Docker 部署与 CI/CD 流水线配置 Streamlit实战 用pycdc批量反编译pyc文件 以ENS 的 BaseRegistrarImplementation 合约为例,用web3.py调用合约 虚拟环境下安装包后,vs code仍然有下滑波浪线及显示找不到包(运行是正常的)的解决办法 Merkle Tree 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(四)可视化 语法树 Solidity开发ERC20智能合约claim token的功能 Solidity开发ERC20智能合约demo及部署到测试网 用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(二)词法分析器
用 C# 开发一个解释器语言——基于《Crafting Interpreters》的实战系列(三)表达式的抽象语法树设计(Expr)
daviyoung · 2025-08-04 · via 博客园 - daviyoung

在前两篇博客中,我们完成了源码的词法分析,将代码拆解为 Token 流,接下来就进入理解如何用代码结构来表示程序中的表达式,也就是抽象语法树(AST)的构建。

本篇重点围绕书中核心数据结构 Expr 类展开,结合 C# 实现,详细讲解它的设计理念、组成部分及其作用。理解了它,就为后续语法分析器和解释器实现打下坚实基础。


为什么需要抽象语法树(AST)?

编程语言的源代码本质上是字符串,但解释器需要有“结构化”语义才能理解执行。抽象语法树正是将线性文本转换成结构化、树状的模型。

举例:

(1 + 2) * 3

文本难以直接表达操作顺序和层次,但 AST 会明确表达“先计算括号内的加法,再乘以3”:

        *
       / \
      +   3
     / \
    1   2

Expr 类的设计理念

Expr 是一个抽象基类,代表所有表达式的通用类型。不同的具体表达式继承自它:

  • Literal:字面量表达式,比如数字、字符串等
  • Binary:二元运算表达式,如加法、乘法
  • Grouping:用括号分组的表达式
  • Unary:一元运算表达式,如取负
  • VariableAssign 等(后续)

访问者模式在 Expr 中的应用

为了对表达式做各种操作(打印、求值、编译等),书中采用了访问者模式(Visitor Pattern),它能避免在表达式类里写死所有逻辑,而是分离操作和数据结构。

Visitor接口定义

public interface Visitor<T>
{
    T VisitBinaryExpr(Binary expr);
    T VisitGroupingExpr(Grouping expr);
    T VisitLiteralExpr(Literal expr);
    T VisitUnaryExpr(Unary expr);
}
  • 这里的泛型 <T> 表示访问方法的返回类型,可以是字符串、求值结果等。

Expr 抽象基类的 Accept 方法

public abstract T Accept<T>(Visitor<T> visitor);
  • 每个具体表达式类都实现此方法,调用对应的访问者方法。

具体表达式类详解

1. Binary(二元表达式)

public class Binary : Expr
{
    public Expr Left { get; }
    public Token Operator { get; }
    public Expr Right { get; }

    public Binary(Expr left, Token op, Expr right)
    {
        Left = left;
        Operator = op;
        Right = right;
    }

    public override T Accept<T>(Visitor<T> visitor)
    {
        return visitor.VisitBinaryExpr(this);
    }
}
  • LeftRight 是操作数,均为 Expr 类型,支持嵌套表达式。
  • OperatorToken,记录运算符本身,比如 +

2. Literal(字面量表达式)

public class Literal : Expr
{
    public object Value { get; }

    public Literal(object value)
    {
        Value = value;
    }

    public override T Accept<T>(Visitor<T> visitor)
    {
        return visitor.VisitLiteralExpr(this);
    }
}
  • 保存字面量值,可以是数字、字符串、布尔等。
  • 访问时直接返回该值。

3. Grouping(分组表达式)

public class Grouping : Expr
{
    public Expr Expression { get; }

    public Grouping(Expr expression)
    {
        Expression = expression;
    }

    public override T Accept<T>(Visitor<T> visitor)
    {
        return visitor.VisitGroupingExpr(this);
    }
}
  • 表示括号内的表达式,用于明确优先级。

4. Unary(一元表达式)

public class Unary : Expr
{
    public Token Operator { get; }
    public Expr Right { get; }

    public Unary(Token op, Expr right)
    {
        Operator = op;
        Right = right;
    }

    public override T Accept<T>(Visitor<T> visitor)
    {
        return visitor.VisitUnaryExpr(this);
    }
}
  • 一元操作符,如负号 -

Expr 的作用和价值

  • 表达式树是解释器的核心数据模型,它将文本代码转换为层次分明的结构。
  • 使用访问者模式后,我们可以灵活地扩展各种表达式操作,比如打印、求值、静态分析。
  • Expr 的设计让代码清晰且易维护,符合面向对象设计原则。