5 声明语句
主要任务
- 为局部名字分配存储单元
- 作用域信息的保存
- 记录类型的管理
符号表条目:(名字、类型、字宽、偏移)
不产生中间代码指令,只更新符号表
块内无变量声明时的翻译
声明只出现在程序最前部
计算被声明名字的类型和相对地址
-
P → { offset = 0 } D ; S // 相对地址初始化为 0,在 enterP 时处理
-
D → D ; D
-
D → id : T { enter (id.lexeme, T.type, offset ) ; offset += T.width } // 更新符号表信息
-
T → integer { T.type = integer ; T.width = 4 } // visitD 时处理,因为只有访问 D 时才知道 D 的结构
-
T → real { T.type = real ; T.width = 8 } // 类型以字宽表示;visitT 时处理
-
T → array [num] of T~1~
{ T.type = array (num.val, T~1~.type) ; T.width = num.val × T~1~.width }
- T → ↑T~1~ { T.type = pointer( T~1~.type ) ; T.width = 4 }
全局变量 offset 记住下一个可用的相对地址
offset 不一定每次都增大,因为堆、栈的增加方向不相同
enter (name, type, offset) 为名字 name 建立符号表条目
允许嵌套定义过程时的翻译
所讨论的语言文法(简单起见,只讨论无参过程)
- P → D ; S
- D → D ; D | id : T | proc id ; D ; S
管理作用域
- 每个过程内声明的符号要置于该过程的符号表中
- 方便地找到子过程和父过程对应的符号表

- 符号表:使用哈希表
- 符号表之间的连接:使用双向链表
- 符号表之间:采用符号表栈
一些符号表相关的函数
- mkTable (previous),建立新的符号表,参数 previous 是先前建立的符号表
- enter (table, name, type, offset),在 table 指向的符号表中建立新条目
- addWidth (table, width),把 table 指向的符号表中所有局部变量条目的累计宽度记录在符号表首部
- enterProc (table, name, newtable),在 table 指向的符号表中为过程名 name 建立新条目,newtable 指向 name 的符号表

上面这个是书上的,可以看出来是后缀形式的翻译方案,下面给的不是后缀形式的,其把中间需要做的语义动作转换成了 M→\epsilon 式的新定义
允许嵌套定义过程时声明语句的处理
- P → D ; S
- D → D ; D | id : T | proc id ; D ; S
一些记号
- tblptr:符号表栈
- offset:偏移量栈
enterP:
t = mkTable(nil);
push(t, tblptr);
push(0, offset);
visitD:
- id : T 更新符号表中 id 对应的条目
enter(top(tblptr), id.lexeme, T.type, top(offset));
top(offset) = top(offset) + T.width;
- proc id ; D ; S
访问 D 前,新建该过程的符号表,进入该过程的作用域
t = mkTable(top(tblptr));
push(t, tblptr);
push(0, offset);
访问 S 后,将该过程符号信息插入父符号表,退出作用域
t = top(tblptr);
addWidth(t, top(offset)); // 因为过程分配了内存,引起了offset下一次分配的变化
pop(tblptr);
pop(offset);
enterProc(top(tblptr), id.lexeme, t); // 现在top(tblptr)是父过程符号表
exitP:
addWidth(top(tblptr), top(offset));
pop(tblptr);
pop(offset);
记录的域名管理
关联的文法
- T → record D end
记录类型单独建符号表(类型表达式),域相对地址从 0 开始
visitT:
访问 D 之前:建立符号表,进入作用域
t = mkTable(nil);
push(t, tblptr);
push(0, offset);
结尾:设置记录的类型表达式和宽度,退出作用域
T.type = record(top(tblptr));
T.width = top(offset);
pop(tblptr);
pop(offset);