符号表系統看起來還算是比較好用,不過為了友善起見,現在在它之上再加一層套。考慮到日後一些類型的文法節點,如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;
}