符号表系统看起来还算是比较好用,不过为了方便起见,现在在它之上再加一层套。考虑到日后一些类型的语法节点,如VariableNode 以及DeclarationNode ,它们直接与符号表打交道的话,就会出现这样的问题:一方面它们要执行语法制导的指令生成,另一方面还需要处理各种跟符号表有关的错误,这样违反了单一责任原则──特别是,当使用 if-else 分支来处理这些错误时,内部的缩进层次太多会使得代码看起来很丑陋。
为了更方便地使用符号表,我们需要在符号表和语法节点的指令生成之间增加一个装饰层,这个装饰器将提供以下便利:
> 容错。当查找(无论是查找类型、首地址,还是维度信息)的符号不存在,或者重复定义符号,它直接将错误送达错误处理模块,并返回一个看似正确的结果。
> 管理符号表层次。当进入一个基本块后,需要在符号表栈内压入一个符号表,而出符号表则相反;另外,在查询变量时,这个中间层会循栈而下,在每个符号表中搜索一遍。
> 编译时的地址计算。当请求一个变量的地址时,特别是数组中的某一个时,要尽可能根据上下文计算其地址。
要达到这些目的,可以考虑这样一组接口
/* st-manager.h */ /* 初始化及反初始化符号表管理模块。 */ void initialSymTabManager(void); void finalizeSymTabManager(void); /* * BasicBlockNode 在进行指令生成时,应该调用这两个函数 * 它们分别表示进入基本块和离开基本块 */ void enterBasicBlock(void); void leaveBasicBlock(void); /* * 声明一个变量并返回其首地址 * 如果一个变量在声明时初始化,那么这样一个返回值将很有帮助 */ int declare(struct VariableNode* var, AcceptType type); /* get the type of a variable. */ AcceptType typeOf(struct VariableNode* var); /* * 给出一个变量节点和一个整数数组,返回其地址 * 对于数组,如果它的第 d 维偏移量是常数,那么返回后,offsets[d] = -1 * 否则 offsets[d] 的值为该数组在这一维的偏移。 */ int staticOffset(struct VariableNode* var, int* offsets);
设置初始化及反初始化是要初始化和反初始化符号表栈。不过,考虑另外一个功能,即当出现未声明的符号时,针对该符号仅报一次错,以后再引用该符号则无视它──gcc就有这个功能──那么我们还得维护一个表,这个表与符号表栈一同初始化。
/* 符号表栈 */ static struct Stack* symTabStack = NULL; /* 未找到的变量链表 */ static struct LinkedList notFoundVars; void initialSymTabManager(void) { symTabStack = newStack(); initLinkedList(¬FoundVars); } void finalizeSymTabManager(void) { struct SymbolTable* table; int i = 0; for (i = 0; i < symTabStack->height(symTabStack); ++i) { table = (struct SymbolTable*)(symTabStack->peekAt(symTabStack, i)); finalizeSymbolTable(table); } symTabStack->finalize(symTabStack); while (0 != notFoundVars.count(¬FoundVars)) { revert(notFoundVars.popElementAt(¬FoundVars, 0)); } notFoundVars.finalize(¬FoundVars); }
接下来是进入和离开符号表
void enterBasicBlock(void) { struct SymbolTable* table = (struct SymbolTable*) (symTabStack->peek(symTabStack)); symTabStack->push(symTabStack, newSymbolTable(table->used + table->baseAddr)); } void leaveBasicBlock(void) { finalizeSymbolTable((struct SymbolTable*)(symTabStack->pop(symTabStack))); }
声明变量
int declare(struct VariableNode* var, AcceptType type) { struct SymbolTable* table = (struct SymbolTable*) (symTabStack->peek(symTabStack)); int addr = table->used + table->baseAddr; struct AbstractValueNode* dimSize; int nrDim = var->dimInfor->length(var->dimInfor), i; int* dims = NULL; // 临时存放维度信息用 if (0 != nrDim) { dims = (int*)allocate(sizeof(int) * nrDim); for (i = 0; i < nrDim; ++i) { dimSize = (struct AbstractValueNode*) (var->dimInfor->peekAt(var->dimInfor, i)); // 必须使用整数来声明数组 // typeOf 是 AbstractValueNode 在语义分析时会加入的一个成员函数,用于确定节点的静态数据类型。 if (INTEGER != dimSize->typeOf(dimSize)) { // 报错,不是整数 } // 检查是否为常数 // staticInt 是 AbstractValueNode 在语义分析时会加入的一个成员函数 // 这个函数用于进行编译时常数折叠优化 if (0 != dimSize->staticInt(dimSize, dims + i)) { // 报错,不是常数 dims[i] = 1; } // 每一维都得大于0 if (0 >= dims[i]) { // 报错 dims[i] = 1; } } } SymbolTableError errCode = regVar(table, var->ident, type, nrDim, dims); if (SymTab_MultiDef == errCode) { // 报错,重复定义 } else if (SymTab_SizeExceeded == errCode) { symError(tooManySymbols, var); } // 注意回收垃圾 if (NULL != dims) { revert(dims); } return addr; }
在实现相关查询之前,现弄这样一个函数notFound ,当变量未定义时,调用这个函数将变量注册进未定义变量表内。
static void notFound(struct VariableNode* var) { struct Iterator* i; for_each (i, ¬FoundVars) { if (0 == strcmp(var->ident, i->current(i))) { i->terminate(i); return; } } // 以前没有出现过,报错 char* ident = (char*)allocate(strlen(var->ident) * sizeof(char)); strcpy(ident, var->ident); notFoundVars.add(¬FoundVars, ident); }
然后是最后两个函数
AcceptType typeOf(struct VariableNode* var) { struct SymbolTable* table; int i; AcceptType type; for (i = 0; i < symTabStack->height(symTabStack); ++i) { table = (struct SymbolTable*)(symTabStack->peekAt(symTabStack, i)); if (SymTab_NotDef != getVarType(table, var->ident, &type)) { return type; } } notFound(var); return INTEGER; } int staticOffset(struct VariableNode* var, int* offsets) { struct SymbolTable* table; int base, nrDim, i, j, count, refArrayDims = var->dimInfor->length(var->dimInfor); struct AbstractValueNode* offset; for (j = 0; j < refArrayDims; ++j) { offsets[j] = -1; } for (i = 0; i < symTabStack->height(symTabStack); ++i) { table = (struct SymbolTable*)(symTabStack->peekAt(symTabStack, i)); if (SymTab_NotDef != getVarNrDim(table, var->ident, &nrDim)) { getVarAddr(table, var->ident, &base); if (nrDim != refArrayDims) { // 报错,数组维度不匹配 return base; } for (j = 0; j < refArrayDims; ++j) { getVarDimSize(table, var->ident, offsets + j, j); offset = (struct AbstractValueNode*) (var->dimInfor->peekAt(var->dimInfor, j)); // 如果某一维编译时可以求出,那么将这一维的偏移总量加入返回值 base 中 // 寻址相关的详细内容,将会在 VariableNode 的语法制导的指令生成中详细说明,敬请期待^,^ if (0 == offset->staticInt(offset, &count)) { base += offsets[j] * count; offsets[j] = -1; } } return base; } } notFound(var); return 0; }