一起打造编译器:语义分析理论与实践(二)

106 阅读1分钟

下面我们继续说说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

IdentifierType
xint
f(int) → void

Symbol Table inside f

IdentifierType
yint

Symbol Table inside For-Loop

IdentifierType
iint
zint

当我们离开代码块for-loop的时候,删除Symbol Table inside For-Loop, 生成一个新的symbol table

IdentifierType
tint

最后,离开函数的时候,我们会只剩下global symbol table.所以你可以说,这是一个栈结构。

  1. 当我们进入新的block,我们push一个新的symbol table
  2. 当我们离开的时候,我们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