跳转至

5 声明语句

主要任务

  1. 为局部名字分配存储单元
  2. 作用域信息的保存
  3. 记录类型的管理

符号表条目:(名字、类型、字宽、偏移)

不产生中间代码指令,只更新符号表

块内无变量声明时的翻译

声明只出现在程序最前部

计算被声明名字的类型和相对地址

  • 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

管理作用域

  • 每个过程内声明的符号要置于该过程的符号表中
  • 方便地找到子过程和父过程对应的符号表

image-20221127223329767

  • 符号表:使用哈希表
  • 符号表之间的连接:使用双向链表
  • 符号表之间:采用符号表栈

一些符号表相关的函数

  • mkTable (previous),建立新的符号表,参数 previous 是先前建立的符号表
  • enter (table, name, type, offset),在 table 指向的符号表中建立新条目
  • addWidth (table, width),把 table 指向的符号表中所有局部变量条目的累计宽度记录在符号表首部
  • enterProc (table, name, newtable),在 table 指向的符号表中为过程名 name 建立新条目,newtable 指向 name 的符号表

image-20221128221820309

上面这个是书上的,可以看出来是后缀形式的翻译方案,下面给的不是后缀形式的,其把中间需要做的语义动作转换成了 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);
本文阅读量