编译器不评估 AST,这就是(天真的)解释器所做的。编译器从 AST 生成代码。
假设一个 AST 节点由一个告诉节点类型的枚举和一个子节点数组组成,评估一个简单的纯 int AST 可能看起来像这样:
int eval_expression(node) {
switch(node.type) {
case ADD:
return eval_expression(node.children[0]) + eval_expression(node.children[1]);
case IF:
if(eval_expression(node.children[0])) {
return eval_expression(node.children[1]);
} else {
return eval_expression(node.children[2]);
}
// and so on
}
}
根据您的语言和表示 AST 的方式,这可能看起来有很大不同,但希望这能给您一个想法。
一个(非常简单的)编译器可能会做什么,看起来更像这样:
void compile_expression(Node node, const char* target_register) {
switch(node.type) {
case ADD:
const char* temp_register = find_unused_register();
compile_expression(node.children[0], target_register);
compile_expression(node.children[1], temp_register);
printf("ADD %s %s\n", target_register, temp_register);
free_register(temp_register);
case IF:
const char* condition_register = find_unused_register();
compile_expression(node.children[0], condition_register);
const char* elseLabel = generate_label();
const char* labelAfterIf = generate_label();
// If the condition was zero, jump to the else case
printf("JZ %s %s\n", condition_register, elseLabel);
compile_expression(node.children[1], target_register);
printf("JUMP %s\n", labelAfterIf);
printf("%s:\n", elseLabel);
compile_expression(node.children[2], target_register);
printf("%s:\n", labelAfterIf);
free_register(temp_register);
// and so on
}
}
上面的代码将汇编代码直接写入标准输出,这不是真正的编译器会做的。它也充满了不良的工程实践,并假设一个相当简化的装配方言作为目标。但希望它能够传达这个想法。
请注意,现实世界的编译器不会直接从 AST 生成程序集(或机器代码),解释器也不会直接评估 AST。相反,两者都将首先从 AST 生成某种形式的中间代码(如三地址代码),然后进一步处理。