动手实现编译器(十七)——局部变量

本节详细介绍了如何在编译器中实现局部变量,包括符号表的修改以区分全局和局部变量,以及堆栈管理以存储局部变量。通过增加`class`和`posn`字段来区分变量类型和位置,同时实现`find_local()`和`new_local()`函数以处理局部符号。此外,更新了语法分析和汇编代码以支持局部变量的声明和访问。测试案例展示了局部变量的正确工作情况。

上一节中,我们已经实现了逻辑运算和负数,在本节中,我们将要实现比较难的局部变量。
现在,我们所有的变量对所有函数都是全局可见的。我们想添加一个本地范围对于变量,这样每个函数都有自己的变量,不能被其他函数看到。此外,在递归函数的情况下,同一个函数的每个实例都有自己的局部变量。
要为同一函数的多个实例创建本地作用域,为了提供一个地方来存储函数的参数,我们需要一个堆。
让我们从局部变量和全局变量之间的区别开始。全局变量必须对所有函数可见,但局部变量只是对一个函数可见。
SubC使用一个符号表来存储有关本地和全局变量。全局变量在一端分配,局部变量存储在另一个。有代码来确保有中间两端没有碰撞。我们可以借鉴这一点。
在将局部符号优先于全局符号方面,我们可以首先搜索符号表的本地端,如果我们没有找到符号,然后我们可以搜索全局端。而且,一旦我们完成解析一个函数,我们可以简单地擦除符号表的本地端。
我们将如何确定参数或局部变量在堆栈中的位置,一旦它们被复制或放置在那里?为此,我将添加一个 posn 字段到每个本地符号表条目。这将指示变量距离栈基指针的偏移地址。

修改符号表

为了区分全局变量和局部变量,我们修改局部变量结构体。

// 变量类型
enum
{
   
   
    C_GLOBAL = 1, // 全局变量
    C_LOCAL       // 局部变量
};

// 符号表结构体
struct symbol_table
{
   
   
    char *name;			        // 符号名
    int type;                   // 类型void或int
    int stype;			        // 结构类型
    int endlabel;			    // 函数的结束标签
    int size;                   // 数组大小
    int class;			        // 符号类别
    int posn;			        // 距离栈基指针的偏移地址
};

我们添加了classposn字段。如上一部分所述,posn为负数,并保存与堆栈基指针的偏移量,即局部变量存储在堆栈中。
在这部分,我只实现了局部变量,没有实现参数。另请注意,我们现在有标记为C_GLOBAL或C_LOCAL的符号。
因此,符号表的名称也发生了变化,其中的索引改变了

int             Globals = 0;		    	// 下一个空闲全局变量槽的位置
int             Locals = SYMBOL_NUM - 1;	// 下一个空闲局部变量槽的位置

从视觉上看,全局符号存储在符号表的左侧,“Globs”指向下一个空闲全局符号槽,而“Locls”指向下一个空闲局部符号槽。

0xxxx......................................xxxxxxxxxxxxNSYMBOLS-1
     ^                                    ^
     |                                    |
   Globals                              Locals

我们现在有的find_global()new_global()函数,用于查找或分配全局符号,我们现在新增find_local()new_local()函数,来查找或分配局部符号。他们有代码来检测LocalsGlobals之间的冲突。

// 检查符号s是否在全局符号表中。
// 返回其插槽位置或-1
int find_global(char *s)
{
   
   
    int i;
    for (i = 0; i < Globals; i++)
    {
   
   
        if (*s == *Tsym[i].name && !strcmp(s, Tsym[i].name))
        return i;
    }
    return -1;
}

// 获取新的全局符号槽的位置
int new_global()
{
   
   
    int p;
    if ((p = Globals++) >= Locals)
    {
   
   
        fprintf(stderr, "Too many global symbols on line %d\n", Line);
        exit(1);
    }
    return p;
}

// 确定符号s是否在本地符号表中,
// 返回其插槽位置或-1
int find_local(char *s)
{
   
   
    int i;
    for (i = Locals + 1; i < SYMBOL_NUM; i++)
    {
   
   
        if (*s == *Tsym[i].name && !strcmp(s, Tsym[i].name))
        return i
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值