我对编译器感兴趣,并且只是为了体验而用C ++编写一个基本编译器!我理解编译器如何解析源代码,然后创建一个令牌树。我不明白的是如何评估该树,然后在必要时返回一个值。例如,如果有一个语句(a + b),我是否有一个函数来处理+令牌,它将传递给a和b?是否会遵循我会对比较操作做同样的事情,然后即使是陈述?
答案 0 :(得分:1)
编译器不评估AST,这就是(天真)解释器的作用。编译器从AST生成代码。
评估一个简单的int-only AST可能看起来像这样,假设一个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
}
}
上面的代码直接将汇编代码写入stdout,这与真正的编译器不同。它也充满了糟糕的工程实践,并假设一个相当简化的汇编方言作为目标。希望它能解决这个问题。
请注意,实际编译器不会直接从AST生成程序集(或机器代码),解释器也不会直接评估AST。相反,两者都将首先从AST生成某种形式的中间代码(如三地址代码),然后再进一步工作。