`
NeuronR
  • 浏览: 60893 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

[符号表]加一层套

阅读更多

    符号表系统看起来还算是比较好用,不过为了方便起见,现在在它之上再加一层套。考虑到日后一些类型的语法节点,如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;
}
分享到:
评论
3 楼 lwwin 2009-06-13  
看了下一回,这里明白了说^^
2 楼 NeuronR 2009-05-25  
是的,跟staticInt一样。。。
1 楼 lwwin 2009-05-24  
declare里面
17. 的typeOf 似乎不是抽象节点的成员函数吧,这里多于了么?

24. 的定义还没有给出吧^^?

相关推荐

    电气技术文字符号.pdf

    - **电源符号**:通常用一个圆圈加一条或两条线表示,具体形状和线条数量取决于电源类型(直流或交流)。 - **接地符号**:一个三角形底部加上一条或多条线段。 - **电阻器符号**:一条或多条波浪线或直线代表不同的...

    电气设计图纸符号

    例如,4*(1*185)+1*95 0.6/1KV表示同一回路四根185mm²电缆加一根95mm²电缆,额定电压0.6/1KV。多芯电缆的表示方法类似,如4**185+1*95 0.6/1KV。 在矿用电缆中,铠装电缆如MKVV22、MKVV32、KVV22、KVV32、KVVR22...

    电缆规格型号

    理解这些符号有助于我们准确地识别电缆的功能特性及其适用范围。 ##### 型号含义: - **R**:表示这是一款连接用的软电缆或电线,具有柔软的结构。 - **V**:代表绝缘材料是聚氯乙烯(Polyvinyl Chloride, PVC)。 ...

    普及组CSP-J第七八套模拟试题模拟题附答案

    ### 普及组CSP-J第七八套模拟试题知识点详解 #### 一、单项选择题知识点解析 **1. 系统软件** - **知识点概述:** - 系统软件是支持计算机系统正常运行的基本软件集合,主要包括操作系统和其他一系列基本工具(如...

    SQL语言书写规则 高手总结

    - **规则**:使用逻辑运算符(`OR`, `IN`, `AND`)和比较运算符(`=`, `, `&gt;=`, `&gt;`, `, `BETWEEN AND`)时,前后各加一个空格。 - **示例**: ```sql WHERE age &gt;= 18 AND salary ; ``` ##### 6. 逗号使用 - **...

    一下两位数加一位数进位加法练习题精选.doc

    锻炼代数思维的题目通过等式来找出代表数值的符号,例如,在等式3+X=9中,学生需要判断X代表的数值。这类问题让学生在加法的基础上,进一步理解等式和变量的关系,为日后的数学学习打下坚实的基础。 自主检测部分则...

    基于AT89S52 单片的频率计

    Protel DXP 是第一套完整的板卡级设计系统,真正实现在单个应用程序中的 集成。设计从一开始的目的就是为了支持整个设计过程,Protel DXP 让你可以 选择最适当的设计途径来按你想要的方式工作。Protel DXP PCB 线路...

    c语言编写单片机技巧

    要实现单片机与电脑的连接,最简单的方式就是采用串口通讯,但需要加一颗RS-232芯片。 17. 在ARM编程中又应当如何? 答:就以嵌入式系统观念为例,一般嵌入式处理器可以分为三类:嵌入式微处理器、嵌入式微...

Global site tag (gtag.js) - Google Analytics