我目前正在研究C下的编译器,我在构建AST数据结构的部分丢失了,特别是对于构建ID结构的部分,它被称为“符号表条目”
我看到网上的结构如:
struct ste {
struct id *name; /* pointer into hash table for assoc. id */
struct decl *decl; /* pointer into symbol table for its decl */
struct ste *prev; /* pointer to previous entry in symbol table */
};
它看起来像一个链表,因为它包含一个指向前一个条目的指针(* prev),但这背后的逻辑是什么?
答案 0 :(得分:8)
您具体问题的答案是:prev链接意味着,当您的代码具有指向其中一个节点的指针时,它可以跟随指向链中前一个链接的链接。符号表可能有这样一个列表的一个原因是处理嵌套范围:
{
int x;
{
int x;
}
}
但是,符号节点可能希望在列表中排列的原因还有很多其他原因。编译器需要访问所有节点的任何原因都是原因。
答案 1 :(得分:2)
你很快就会看到来自C程序员的有害习惯的残羹剩饭:假设符号将出现在某些列表中,而不是单独分配列表结构,列表指针作为符号结构的一部分包含在内。这个技巧为每个列表元素保存了一个分配,但代价是:符号可以打开的列表集是固定的,这种结构使程序员感到困惑。如果应用程序是编译器,则没有理由再次使用此技巧。更清楚的是有一个单独的列表结构定义如下:
struct ste_list {
struct ste *symbol_table_entry;
struct str_list *next;
};
你可以随心所欲地拥有这些,没有人更聪明。而你发现的内部指针会让人感到困惑。
你问
这背后的逻辑是什么?
部分答案很简单,在尊贵列表上添加符号很有用。在不了解特定编译器的情况下,我无法明确地回答这个问题。我最好的猜测是prev
条目将用于实现嵌套作用域(C中的{ ... }
括号),但这是基于我见过或编写过的编译器的猜测。因此,逻辑可能是当遇到右括号时,编译器可能会跟随该链接,直到它到达封闭范围中的ste
。与您正在研究的编译器的作者相比,具有更多经验的人通常会将此逻辑放在“符号表抽象”中,其中包括enterscope()
和exitscope()
等函数,以及这些操作将从各个符号表条目的内部表示中隐藏。
答案 2 :(得分:1)
我首先考虑使用反向链表来支持那些支持覆盖变量名的语言,例如:
int main (void) {
int x = 1;
int y = 1;
if (x == 1) {
int y = 2;
printf ("y = %d\n", y);
}
return 0;
}
在这种情况下,您希望访问具有最里面范围的变量(最后一个定义的变量)。这可以通过向后走过列表找到(假设您正在通过前进构建列表)。
然后,当范围消失时,您也可以调整“头部”指针以删除最近添加的变量。
当然,你可以通过在当前头部之前插入而不是添加到列表的末尾(这看起来像概念正在做什么,只需使用名为{的指针来实现相同的效果。 {1}}代替prev
)。