compiler-go/doc/三地址生成说明.md

3.0 KiB
Raw Blame History

通过例子来说明如何通过语法树实现三地址代码的生成。 假设有一个非常简单的算术表达式语言,它支持加法和变量赋值。 目标是将这个表达式转换为三地址代码。

步骤 1: 定义语法规则

首先需要定义这个简单语言的语法规则

grammar SimpleLang;

stat:   assign
    |   expr
    ;

assign: ID '=' expr ';' { /* 生成赋值的三地址代码 */ }
    ;

expr:   expr '+' expr
    |   num
    ;

num:   INT { /* 生成数字的三地址代码 */ }
    ;

ID  :   [_a-zA-Z] [_a-zA-Z0-9]*
    ;

INT :   [0-9]+
    ;

步骤 2: 生成解析器代码

使用ANTLR工具根据上述语法规则文件生成对应的词法分析器和语法分析器代码。

步骤 3: 遍历解析树

接下来遍历解析树来生成三地址代码。创建一个监听器或访问者来实现这一过程。 以下是一个访问者实现,遍历解析树并生成三地址代码:

class SimpleLangVisitor : public SimpleLangBaseVisitor<ThreeAddressCode*> {
public:
    // 访问数字节点时调用
    ThreeAddressCode* visitNum(SimpleLangParser::NumContext* ctx) {
        string value = ctx->INT()->getText();
        return new ThreeAddressCode("MOV", "R0", value); // 假设R0是临时寄存器
    }

    // 访问加法表达式节点时调用
    ThreeAddressCode* visitExpr(SimpleLangParser::ExprContext* ctx) {
        // 假设左右子树已经生成了三地址代码
        ThreeAddressCode* left = visit(ctx->expr(0));
        ThreeAddressCode* right = visit(ctx->expr(1));
        // 生成加法的三地址代码
        return new ThreeAddressCode("ADD", left->dest, right->dest);
    }

    // 访问赋值节点时调用
    ThreeAddressCode* visitAssign(SimpleLangParser::AssignContext* ctx) {
        string varName = ctx->ID()->getText();
        ThreeAddressCode* exprCode = visit(ctx->expr());
        // 生成赋值的三地址代码
        return new ThreeAddressCode("MOV", varName, exprCode->dest);
    }
};

步骤 4: 生成三地址代码

在上述访问者中,我们定义了如何生成数字、加法表达式和赋值语句的三地址代码。例如,对于数字,我们生成一个将数字移动到临时寄存器的指令。对于加法,我们生成一个将两个操作数相加的指令。对于赋值,我们生成一个将表达式的结果移动到变量的指令。

示例

假设我们有如下的源代码:

x = 3 + 4;

解析树将如下所示:

   assign
    /   \
  ID     expr
       /   \
    '='   +
       /   \
     num   num
     |     |
    3     4

遍历这个解析树,我们将生成以下三地址代码:

  1. MOV R0, 3 将数字3移动到临时寄存器R0
  2. MOV R1, 4 将数字4移动到另一个临时寄存器R1
  3. ADD R0, R0, R1 将R0和R1相加结果存回R0
  4. MOV x, R0 将R0的值移动到变量x

这样,我们就通过语法树实现了从源代码到三地址代码的转换。这个过程可以根据具体的语言和需求进行扩展和修改。