您需要将标识符映射到类型/含义,每个范围实例在源代码中找到,由语言定义。有些人会将这样一个单一的映射称为“符号表”,但我认为这是对该术语的滥用;对于此类单独的范围图,我更喜欢术语“符号空间”。对我来说,范围映射集就是符号表。
符号空间/表的概念与编译器创建此类符号空间/表的方式和时间无关。
对于经典的类似 Pascal 的语言,范围恰好以与程序嵌套语法相匹配的方式嵌套(“词法范围”)。对于此类语言,可以以类似堆栈的方式创建符号空间,从上到下(从最低行号到最大行号)处理程序。当遇到每个新的范围边界时,一个新的符号空间被压入堆栈。标识符查找通过搜索当前符号空间(堆栈顶部的符号空间)来进行,如果未找到标识符,则在堆栈的下方搜索符号空间。当一个范围退出时,该符号空间可以被删除(例如,弹出)。此方案仅在编译器单程处理程序时才有效。有时您希望保留所有符号空间以允许在多遍中对程序进行复杂处理;在这种情况下,
许多语言的范围规则根本不适合符号堆栈空间。一个明显的例子是命名空间,它实际上只是可以从程序的广泛分离部分访问的符号空间。
我使用工具基础架构构建了大量需要符号表的语言处理工具(有关该工具的详细信息,请参阅 bio)。大多数情况下,我不使用堆栈样式的符号空间,并且工具基础结构可以直接创建(并在必要时删除)符号空间,并提供一个符号空间和另一个符号空间之间的链接。其中一个链接是一个特殊的词法范围链接,使基础设施能够记录一个符号空间在词法上嵌入另一个符号空间。然后,标准查找方案搜索“当前”范围,如果没有找到匹配的符号,则按照词法范围链接继续搜索。
该方案实际上更聪明一点。每个符号空间都有一个关联的父符号空间链接序列;当在当前范围内找不到符号时,标准词法搜索会(递归地)访问父代。只有一个父链接,这与词法范围相同。使用多个父链接,它可以很好地处理多重继承。命名空间是通过使用“知名”顶级符号空间处理的,其中包含顶级命名空间的符号等。您可以在这个 SO 答案中看到 C++ 的样子:https ://stackoverflow.com/一个/32012786/120163
在访问词法范围链接之前调用自定义操作(“程序附件”);这允许操作决定首先访问任意其他符号空间。特别是,这使得 Java 的符号空间搜索可以从文件系统中获取源/类文件,提取它们的符号,然后根据需要继续搜索。