下面我们继续说说scope
Nesting scopes
很多语言是支持无限数量的scope的。 比如
int x;
void f(int y) {
for(int i=0;i<10;i++) {
int z = 10;
}
int t = 2;
}
Global symbol table
| Identifier | Type |
|---|---|
| x | int |
| f | (int) → void |
Symbol Table inside f
| Identifier | Type |
|---|---|
| y | int |
Symbol Table inside For-Loop
| Identifier | Type |
|---|---|
| i | int |
| z | int |
当我们离开代码块for-loop的时候,删除Symbol Table inside For-Loop, 生成一个新的symbol table
| Identifier | Type |
|---|---|
| t | int |
最后,离开函数的时候,我们会只剩下global symbol table.所以你可以说,这是一个栈结构。
- 当我们进入新的block,我们push一个新的symbol table
- 当我们离开的时候,我们pop最新的那个item
Spaghetti stack
A spaghetti stack is an N-ary tree data structure in which child nodes have pointers to the parent nodes (but not vice-versa)
如果使用上边的方法实现symbol table, 会有一个缺点,如果我们需要多次遍历AST, 我们必须每一次都要创建一个symbol table。这回显著减低我们的效率,所以最好是使用spaghetti stack.
Dynamic scopes
有的编程语言很特殊,比如Lisp,scope需要通过执行代码才能确定。考虑下面的程序:
int x;
void g() {
x = 10;
}
void f() {
int x;
g();
// local variable x now has the value 10
}
这个程序就会导致本地变量x变为10。 这个就是动态scope.这个不是流行的方法,因为我们在执行程序的时候,还要考虑symbol table, 而java,c可以在编译的时候就确定这些信息。
Conclusion
在做语义分析的时候,我们必须啊考虑上边的这些问题。最后在总结一下
- 在哪个scope可以找到变量。全局还是局部?
- 考虑nested scopes?
- 允许不同scope使用同样的变量名吗?
- forward referencing?
- static vs Dynamic scopes?
之后我会详细介绍一下Type Checking