如何在多通道编译器中正确重用符号表

问题描述

我目前正在为块结构语言语言构建多通道编译器。我在语义分析阶段建立了一个范围堆栈。当进入一个新的作用域时,创建一个新表,将其压入堆栈并使其成为当前作用域表,然后将这个作用域内的所有符号插入到当前表中。 离开作用域时,将当前表记录在AST节点中,然后从作用域栈中弹出。

这样,在代码生成阶段,就不用重新构建符号表了。一旦它进入一个新的范围,它可以简单地从 AST 节点获取表,然后将其推送到范围堆栈。我想这是大多数编译器教科书都推荐的方式。

在大多数情况下,这工作得很好,但是,有一个极端情况我不知道如何正确处理。考虑以下代码示例:

int a = 1;
int b = 2;

void test()
{
    int a = b;
    int b = 3;
}

它有两个作用域:全局作用域和 test() 的作用域。 因此,要为 test() 生成代码,我们必须:

  1. 将全局符号表推送到作用域堆栈
  2. 从 AST 节点获取 test() 的符号表并将其推送到作用域堆栈

现在,当处理“int a = b;”时,它会从作用域堆栈中找到局部变量b,这显然是不正确的,因为局部b尚未声明。

知道如何处理这个问题吗? 离开作用域时是否必须销毁所有符号并在代码生成阶段重新构建符号表?

谢谢各位!

解决方法

此问题的一个解决方案是让标识符的 AST 节点包含指向创建时在符号表中找到的特定符号的链接。假设程序文本从头到尾依次解析,一次一个语句,这将给出正确的符号。这也消除了在符号表中重复查找的需要。