访问者模式和编译器代码生成,如何处理赋值?
Visitor pattern and compiler code generation, how to process assignment?
为了用我的编程语言生成代码,我使用的是访问者模式,我想找到一种更好的方法来处理赋值语句。
我的虚拟机是基于注册的,每个访问的表达式节点只是将一个寄存器号推送到全局堆栈中,所以当我访问二进制表达式节点时,我执行如下代码:
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
和相应类型的第二个参数的东西。
那么您就可以避免当前代码中存在的所有检查。此外,根据您的语言允许的目标种类,分配目标的处理可以使用一组不同的遍历函数,不同的访问者,或者带有指示正在处理目标的标志的同一访问者,而不是正则表达式.哪种方法更好取决于您需要生成的语言和代码。
为了用我的编程语言生成代码,我使用的是访问者模式,我想找到一种更好的方法来处理赋值语句。
我的虚拟机是基于注册的,每个访问的表达式节点只是将一个寄存器号推送到全局堆栈中,所以当我访问二进制表达式节点时,我执行如下代码:
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
和相应类型的第二个参数的东西。
那么您就可以避免当前代码中存在的所有检查。此外,根据您的语言允许的目标种类,分配目标的处理可以使用一组不同的遍历函数,不同的访问者,或者带有指示正在处理目标的标志的同一访问者,而不是正则表达式.哪种方法更好取决于您需要生成的语言和代码。