天天看點

[符号表]加一層套

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

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(&notFoundVars)) {
        revert(notFoundVars.popElementAt(&notFoundVars, 0));
    }

    notFoundVars.finalize(&notFoundVars);
}

      

接下來是進入和離開符号表

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, &notFoundVars) {
        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(&notFoundVars, 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;
}