




















在前两篇博客中,我们完成了源码的词法分析,将代码拆解为 Token 流,接下来就进入理解如何用代码结构来表示程序中的表达式,也就是抽象语法树(AST)的构建。
本篇重点围绕书中核心数据结构 Expr 类展开,结合 C# 实现,详细讲解它的设计理念、组成部分及其作用。理解了它,就为后续语法分析器和解释器实现打下坚实基础。
编程语言的源代码本质上是字符串,但解释器需要有“结构化”语义才能理解执行。抽象语法树正是将线性文本转换成结构化、树状的模型。
举例:
(1 + 2) * 3
文本难以直接表达操作顺序和层次,但 AST 会明确表达“先计算括号内的加法,再乘以3”:
*
/ \
+ 3
/ \
1 2
Expr 是一个抽象基类,代表所有表达式的通用类型。不同的具体表达式继承自它:
为了对表达式做各种操作(打印、求值、编译等),书中采用了访问者模式(Visitor Pattern),它能避免在表达式类里写死所有逻辑,而是分离操作和数据结构。
public interface Visitor<T>
{
T VisitBinaryExpr(Binary expr);
T VisitGroupingExpr(Grouping expr);
T VisitLiteralExpr(Literal expr);
T VisitUnaryExpr(Unary expr);
}
<T> 表示访问方法的返回类型,可以是字符串、求值结果等。public abstract T Accept<T>(Visitor<T> visitor);
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);
}
}
Left 和 Right 是操作数,均为 Expr 类型,支持嵌套表达式。Operator 是 Token,记录运算符本身,比如 +。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);
}
}
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);
}
}
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 的设计让代码清晰且易维护,符合面向对象设计原则。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。