访客模式和编译器代码生成,如何处理赋值



对于我的编程语言中的代码生成,我正在使用访问者模式,我想找到一种更好的方法来处理赋值语句。

我的虚拟机是基于注册的,访问的每个表达式节点只需将寄存器编号推送到全局堆栈中,因此当我访问二进制表达式节点时,我会执行如下代码:

static void visit_binary_expr (gvisitor_t *self, gnode_binary_expr_t *node) {
    DECLARE_CODE();
    bool is_assignment = (node->op == TOK_OP_ASSIGN);
    if (is_assignment) {
        // assignment is right associative
        visit(node->right);
        visit(node->left);
    } else {
        // visiting binary operation from left to right
        visit(node->left);
        visit(node->right);
    }
    if (!is_assignment) {
        uint32_t r3 = ircode_register_pop(code);
        uint32_t r2 = ircode_register_pop(code);
        uint32_t r1 = ircode_register_push_temp(code);
        opcode_t op = token2opcode(node->op);
        ircode_add(code, op, r1, r2, r3);
    }
}

使用此代码,我可以处理以下指令:a + b假设寄存器 1 中的变量 a 和寄存器 2 中的变量 b 生成的代码将为:

ADD 3 1 2

问题在于分配需要一组不同的指令,并且仅在堆栈上具有寄存器编号是不够的。例如,为了访问(读取)全局变量,我应该使用 GLOAD 指令,而要存储(写入)到全局变量中,我应该使用 GSTORE 指令。

我目前正在通过将布尔is_assignment值存储到每个节点来解决这个问题,因此我可以递归检查要生成的指令,但这需要将大量逻辑分布到每个访问的节点中,我真的很想找到一种更优雅的方式,只有 visit_binary_expr 函数才能决定生成的最佳指令。

由于赋值与其他二元操作完全不同(它具有更改左侧操作数的副作用),因此将其作为完全独立的操作进行处理是有意义的,与二进制操作完全无关。在这种情况下,您将拥有类似 visit_assignment 的相应类型的第二个参数。

然后,您可以避免当前代码中存在的所有检查。此外,根据语言允许的目标类型,分配目标的处理可以使用一组不同的遍历函数、不同的访问者或具有指示正在处理目标的标志的同一访问者,而不是正则表达式。决定哪种方法更好取决于您需要生成的语言和代码。

最新更新