# 第5章 AST:抽象语法树的设计与构建

# 一、前言

AST 是对解析树的“语义化”抽象,去掉语法噪音,仅保留对后续阶段重要的结构。

# 二、目标

  • 理解 AST 与 Parse Tree 的差异
  • 掌握左结合折叠、位置信息收集
  • 能用 dump-ast 观察抽象结构

# 三、设计

术语说明:

  • SourcePos:源位置 line:column
  • 左结合折叠:链式二元运算按左结合构造嵌套 BinaryExpr

核心流程图:

架构交互图:

# 四、实现

目录树(关注项):

src/main/java/com/lxg/ast/node
src/main/java/com/lxg/ast/expr
src/main/java/com/lxg/ast/stmt
src/main/java/com/lxg/ast/program
src/main/java/com/lxg/frontend/AstBuilder.java

命令:

java -jar target/my-language-0.1.0-SNAPSHOT.jar examples/conditions.lxg --dump-ast

代码对照:左结合的 addition 折叠

/** addition:处理 + -,按左结合折叠为 BinaryExpr。 */
@Override
public Object visitAddition(LxgParser.AdditionContext ctx) {
    Object left = visit(ctx.multiplication(0));
    for (int i = 1; i < ctx.multiplication().size(); i++) {
        String opText = ctx.getChild(2 * i - 1).getText();
        Object right = visit(ctx.multiplication(i));
        BinaryOp op = "+".equals(opText) ? BinaryOp.ADD : BinaryOp.SUB;
        left = new BinaryExpr(pos(ctx.getStart()), (Expression) left, op, (Expression) right);
    }
    return left;
}

代码对照:primary 中的字符串反转义(完整实现)

/**
 * 反转义字符串字面量:将转义序列替换为实际字符。
 * 支持:\n 换行、\r 回车、\t 制表、\" 双引号、\\ 反斜杠。
 */
private static String unescapeString(String s) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '\\' && i + 1 < s.length()) {
            char n = s.charAt(++i);
            switch (n) {
                case 'n': sb.append('\n'); break;
                case 'r': sb.append('\r'); break;
                case 't': sb.append('\t'); break;
                case '"': sb.append('"'); break;
                case '\\': sb.append('\\'); break;
                default: sb.append(n); break;
            }
        } else {
            sb.append(c);
        }
    }
    return sb.toString();
}

# 五、测试

  • 运行:mvn -q -Dtest=AstPrinterTest test
  • 检查点:Let/If/Print 等关键结构是否打印

# 六、总结

  • AST 是“语义友好”的中间形态,建立起后续语义与生成的桥梁;保证 SourcePos 全链路传递