1.1 符号表设计
1.1.1 主符号表分析
主符号表首先需要保存全局常量、变量,子函数/过程。其次,为了保证库程序名、主程序名、主程序参数名不被重定义,主符号表中还需保存库程序项、主程序名项、主程序参数项。
各成分的存储顺序如下图所示:
1.1.2 子符号表分析
子符号表中,除了需要保存局部常量、变量等,还需保存子程序参数(传值参数、引用参数),当然为了避免子程序中的标识符与当前子程序名同名,还需将子程序名保存到子符号表中;由于我们不支持函数/过程的嵌套定义,所以子符号表不包含子程序这一项。
各成分的存储顺序如下图所示:
1.1.3 符号表分析总结
符号表的第0个位置,存放的必然是该符号表对应的程序名,第1到第amount个位置,存放的必然是该符号表对应的程序所定义的amount个参数;另外主符号表还将read、write、writeln、exit存放在了第amount+1到第amount+4个的位置。主符号表和子符号表接下来的位置都先存放了所有的常量定义,再存放了所有的变量定义。最后主符号表还保存了所有的子程序定义,按照文法规定,子过程和子函数的存放位置没有必然的先后关系,可以混合存储。
1.1.4 在符号表中存放当前程序名的必要性
首先需要保证程序定义中的所有符号都不能和程序名同名,这一点就足以支撑将当前程序名加入当前符号表,且需放在一开始的位置;其次,赋值语句有一种很特殊的情况,就是将函数名作为左值,表达式作为右值的情况,这实际上是一条返回值语句,在进行类型检查时,需要查符号表获得左值的类型,那么如果已经将当前的函数名保存在了当前的子符号表中,将检查类型检查中需要特判的情况,便于程序的编写。
1.1.5 表项分析
综合上述分析,我们发现主符号表和子符号表的逻辑结构差别不大,所以表类和表项类均统一设计。各种不同的标识符分析如下表所示:
标识符种类 | 是否可能出现在主符号表中 | 是否可能出现在子符号表中 | 附加属性 |
---|---|---|---|
变量 | √ | √ | 变量类型 |
常量 | √ | √ | 常量类型、常量值(需根据常量类型设置不同的域)、常量是否是负数 |
数组 | √ | √ | 数组元素类型、数组维数、数组各维上下界 |
传值参数 | × | √ | 类型 |
引用参数 | × | √ | 类型 |
过程 | √ | × | 参数个数、指向子符号表的指针 |
函数 | √ | × | 参数个数、返回值类型、指向子符号表的指针 |
主/子程序名 | √ | √ | 程序类型(函数还是过程)、程序参数个数、返回值类型(如果是函数,则该项有意义) |
主程序参数 | √ | × | 无(主程序参数不带类型) |
1.1.6 表类定义
1.1.6.1 变量
名字 | 类型 | 描述 |
---|---|---|
tableType | string | 主符号表标识为“main”,子符号表标识为“sub” |
recordList | vector<_SymbolRecord*> | _SymbolRecord类型的vector,_SymbolRecord即表项类。 用于存储每一个标识符的信息,符号表记录类型的数据结构会在下文具体描述 |
idToLoc | map |
存储标识符id及其位置对应关系的map结构。每加入一个符号,都要将其id和其所在recordList中的下标存储到该map中,可以加快查询速度。 |
1.1.6.2 添加传值参数方法
- 接口
void addPara(string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 传值参数的id | | int lineNumber | 传值参数所在行号 | | string type | 传值参数的类型 |
函数功能
将传值参数类型的标识符加到符号表中
- 函数伪代码
void _SymbolTable::addPara(string id, int lineNumber, string type) { 遍历参数列表,查看当前标识符是否在符号表中已有记录 { 若已有记录,在当前标识符id的前面添加存储次数个_; } 新建符号表记录; 调用setPara将传值参数的id、行号、传值参数类型写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; 若当前标识符在符号表中已有记录,存储次数加1; 若当前标识符在符号表中未有记录,存储次数为1; }
1.1.6.3 添加引用参数方法
- 接口
void addVarPara(string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 传引用参数的id | | int lineNumber | 传引用参数所在行号 | | string type | 传引用参数的类型 |
函数功能
将传引用参数类型的标识符加到符号表中
- 函数伪代码
void _SymbolTable::addVarPara(string id, int lineNumber, string type) { 遍历参数列表,查看当前标识符是否在符号表中已有记录 { 若已有记录,在当前标识符id的前面添加存储次数个_; } 新建符号表记录; 调用setVarPara将传引用参数的id、行号、传引用参数类型写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; 若当前标识符在符号表中已有记录,存储次数加1; 若当前标识符在符号表中未有记录,存储次数为1; }
1.1.6.4 添加普通变量方法
- 接口
void addVar (string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 变量的id | | int lineNumber | 变量所在行号 | | string type | 变量的类型 |
函数功能
将变量类型的标识符加到符号表中
void _SymbolTable::addVar (string id, int lineNumber, string type) {
新建符号表记录;
调用setVar将变量的id、行号、变量类型写入记录中;
将记录添加到当前符号表中;
将记录的位置保存到idToLoc中;
}
1.1.6.5 添加常量方法
- 接口
void addConst(string id, int lineNumber, string type, bool isMinusShow, string value)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 常量的id | | int lineNumber | 常量所在行号 | | string type | 常量的类型 | | bool isMinusShow | 用于判断常量标识符符号正负的bool类型量 | | string value | 常量标识符的值 |
函数功能
将常量类型的标识符加到符号表中
函数伪代码
void _SymbolTable::addConst(string id, int lineNumber, string type, bool isMinusShow, string value ) { 新建符号表记录; 调用setConst将常量的id、行号、常量类型、判断正负号的bool量、 常量的值写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; }
1.1.6.6 添加数组方法
接口
void addArray (string id, int lineNumber, string type, int amount, vector< pair
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 数组的id | | int lineNumber | 数组所在行号 | | string type | 数组的类型 | | int amount | 数组的维数 | | vector< pair
> arrayRangeList | 存储数组各维上下界的vector | 函数功能
将数组类型的标识符加到符号表中
- 函数伪代码
void _SymbolTable::addArray(string id, int lineNumber, string type, int amount, vector< pair<int, int> > arrayRangeList) { 新建符号表记录; 调用setArray将数组的id、行号、数组元素类型、数组维数、数组各维上下界写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; }
1.1.6.7 添加过程方法
- 接口
void addProcedure(string id, int lineNumber, int amount, _SymbolTable *subSymbolTable)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | Procedure的id | | int lineNumber | Procedure所在行号 | | int amount | Procedure参数的个数 | | _SymbolTable *subSymbolTable=NULL | Procedure子符号表的指针 |
函数功能
将Procedure类型的标识符加到符号表中
- 函数伪代码
void _SymbolTable::addProcedure(string id, int lineNumber, int amount, _SymbolTable *subSymbolTable) { 新建符号表记录; 调用setProcedure将过程的id、行号、过程参数个数、指向该过程子符号表的指针写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; }
1.1.6.8 添加函数方法
- 接口
void addFunction(string id, int lineNumber, string type, int amount, _SymbolTable *subSymbolTable)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | Function的id | | int lineNumber | Function所在行号 | | string type | Function返回值的类型 | | int amount | Function参数的个数 | | _SymbolTable *subSymbolTable=NULL | Function子符号表的指针 |
函数功能
将Function类型的标识符加到符号表中
- 函数伪代码
void _SymbolTable::addFunction(string id, int lineNumber, string type, int amount, _SymbolTable *subSymbolTable=NULL) { 新建符号表记录; 调用setFunction将函数的id、行号、函数返回值类型、函数参数个数、 指向该函数子符号表的指针写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; }
1.1.6.9 添加子程序符号表指针方法
- 接口
addSubSymbolTable(string id, _SymbolTable *subSymbolTable)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 子符号表的指针的id | | _SymbolTable *subSymbolTable=NULL | 指向子符号表的指针 |
函数功能
将指向子符号表的指针加到主符号表中
- 函数伪代码
void _SymbolTable::addSubSymbolTable(string id, _SymbolTable* symbolTable) { 判断idToloc中id是否已经存在, 若存在,则将idToLoc[id]相应内容作为recordList的下标,将指向子符号表的指针放到符号表中; 若不存在,则报错:子程序不存在; }
1.1.6.10 添加主/子程序名方法
- 接口
addProgramName(string id, int lineNumber, string subprogramType, int amount, string returnType)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 程序名的id | | int lineNumber | 程序名所在行号 | | string subprogramType | 该program的类型,只有Function和Procedure两种 | | int amount | 该program的参数个数 | | string returnType | 该program的返回值类型,若为Procedure,其返回值类型为void |
函数功能
将当前program的程序名添加到其符号表第0个位置上
- 函数伪代码
void _SymbolTable::addProgramName(string id, int lineNumber, string subprogramType, int amount, string returnType) { 判断记录列表是否为空,若不为空,则报错; 遍历参数列表,查看当前标识符是否在符号表中已有记录 { 若已有记录,在当前标识符id的前面添加存储次数个_; } 新建符号表记录; 调用setProgramName将当前Program的id、行号、Program的类型(函数还是过程)、Program参数个数、Program返回值类型写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; 若当前标识符在符号表中已有记录,存储次数加1; 若当前标识符在符号表中未有记录,存储次数为1; }
1.1.6.11 添加主程序参数方法
- 接口
addVoidPara(string id, int lineNumber)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 主程序参数的id | | int lineNumber | 主程序参数所在行号 |
函数功能
将主程序参数加入到符号表中
- 函数伪代码
void _SymbolTable::addVoidPara(string id, int lineNumber) { 遍历参数列表,查看当前标识符是否在符号表中已有记录 { 若已有记录,在当前标识符id的前面添加存储次数个_; } 新建符号表记录; 调用setVoidPara将主函数参数的id、行号写入记录中; 将记录添加到当前符号表中; 将记录的位置保存到idToLoc中; 若当前标识符在符号表中已有记录,存储次数加1; 若当前标识符在符号表中未有记录,存储次数为1; }
1.1.7 表项类定义
1.1.7.1 变量
名字 | 类型 | 描述 |
---|---|---|
flag | string | “value parameter”表示传值参数, “var parameter”表示传引用参数, “normal variant”表示普通变量, “constant”表示常量, “array”表示数组, “procedure”表示过程, “function”表示函数 “(sub)program name”表示该条记录是当前符号表对应的程序名信息 “parameter of program”表示主程序的参数 |
id | string | 当前标识符的id |
lineNumber | int | 当前标识符定义位置的行号 |
type | string | 如果是变量/常量,则表示变量/常量类型; 如果是数组,则表示数组元素的类型; 如果是函数,则表示函数返回值类型,类型只能为基本类型,”integer”,”real”,”char”,”boolean” |
value | string | 如果当前标识符是常量,则表示常量取值,这里面存的一定是无符号数(也就是非负数) |
isMinusShow | bool | 表示常量前是否带负号,换句话说表示常量是否是负数 |
amount | int | 如果是数组,则表示数组维数,如果是函数/过程,则表示参数个数 |
arrayRangeList | vector< pair |
数组各维上下界 |
subSymbolTable | _SymbolTable* | 指向过程/函数对应的子符号表的指针 |
subprogramType | string | 这是一条特殊的记录,表示当前符号表对应的程序名称等信息,该变量表示程序是函数还是过程 |
1.1.7.2 各种类标识符与其拥有的属性情况
flag | id | lineNumber | type | value | isMinusShow |
---|---|---|---|---|---|
value parameter | √ | √ | √ | × | × |
var parameter | √ | √ | √ | × | × |
normal variant | √ | √ | √ | × | × |
constant | √ | √ | √ | √ | √ |
array | √ | √ | √ | × | × |
procedure | √ | √ | × | × | × |
fucntion | √ | √ | √ | × | × |
(sub)program name | √ | √ | × | × | × |
parameter of program | √ | √ | × | × | × |
flag | amount | arrayRangeList | subSymbolTable | subprogramType |
---|---|---|---|---|
value parameter | × | × | × | × |
var parameter | × | × | × | × |
normal variant | × | × | × | × |
constant | × | × | × | × |
array | √ | √ | × | × |
procedure | √ | × | √ | × |
fucntion | √ | × | √ | × |
(sub)program name | √ | × | × | √ |
parameter of program | × | × | × | × |
1.1.7.3 设置为传值参数方法
- 接口
void setPara(string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 传值参数的id | | int lineNumber | 传值参数所在行号 | | string type | 传值参数的类型 |
函数功能
设置传值参数类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setPara(string id, int lineNumber, string type) { 将当前记录的flag设为"value parameter"; 将传入的传值参数id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的传值参数类型赋值给将当前记录的type; }
1.1.7.4 设置为引用参数方法
- 接口
void setVarPara(string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 传引用参数的id | | int lineNumber | 传引用参数所在行号 | | string type | 传引用参数的类型 |
函数功能
设置传引用参数类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setPara(string id, int lineNumber, string type) { 将当前记录的flag设为" var parameter"; 将传入的传引用参数id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的传引用参数类型赋值给将当前记录的type; }
1.1.7.5 设置为普通变量方法
- 接口
void setVar (string id, int lineNumber, string type)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 变量的id | | int lineNumber | 变量所在行号 | | string type | 变量的类型 |
函数功能
设置变量类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setVar(string id, int lineNumber, string type) { 将当前记录的flag设为" normal variant"; 将传入的变量id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的变量类型赋值给将当前记录的type; }
1.1.7.6 设置为常量方法
- 接口
void setConst(string id, int lineNumber, string type, bool isMinusShow, string value)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 常量的id | | int lineNumber | 常量所在行号 | | string type | 常量的类型 | | bool isMinusShow | 用于判断常量标识符符号正负的bool类型量 | | string value | 常量标识符的值 |
函数功能
设置常量类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setConst(string id, int lineNumber, string type, bool isMinusShow, string value) { 将当前记录的flag设为"constant"; 将传入的常量id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的常量类型赋值给将当前记录的type; 将传入的记录常量正负号的isMinusShow赋值给当前记录的isMinusShow; 将传入的常量的值赋值给当前记录的值; }
1.1.7.7 设置为数组方法
- 接口
void setArray (string id, int lineNumber, string type, int amount, vector< pair
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 数组的id | | int lineNumber | 数组所在行号 | | string type | 数组的类型 | | int amount | 数组的维数 | | vector< pair
> arrayRangeList | 存储数组各维上下界的vector | 函数功能
设置数组类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setArray(string id, int lineNumber, string type, int amount, vector< pair<int,int> > arrayRangeList) { 将当前记录的flag设为"array"; 将传入的数组id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的数组元素类型赋值给将当前记录的type; 将传入的数组维数赋值给当前记录的amount; 将传入的数组记录各维上下界的值的vector赋值给当前记录的arrayRangeList; }
1.1.7.8 设置为过程方法
- 接口
void setProcedure(string id, int lineNumber, int amount, _SymbolTable *subSymbolTable=NULL)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | Procedure的id | | int lineNumber | Procedure所在行号 | | int amount | Procedure参数的个数 | | _SymbolTable *subSymbolTable=NULL | Procedure子符号表的指针 |
函数功能
设置Procedure类型标识符所在记录的具体信息
函数伪代码
void _SymbolRecord::setProcedure(string id, int lineNumber, int amount, _SymbolTable *subSymbolTable) { 将当前记录的flag设为"procedure"; 将传入的过程id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的过程参数个数赋值给当前记录的amount; 将传入的指向该过程子符号表指针赋值给当前记录的subSymbolTable; }
1.1.7.9 设置为函数方法
接口
void setFunction(string id, int lineNumber, string type, int amount, _SymbolTable *subSymbolTable=NULL)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | Function的id | | int lineNumber | Function所在行号 | | string type | Function返回值的类型 | | int amount | Function参数的个数 | | _SymbolTable *subSymbolTable=NULL | Function子符号表的指针 |
函数功能
设置Function类型标识符所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setFunction(string id, int lineNumber, string type, int amount, _SymbolTable *subSymbolTable) { 将当前记录的flag设为"function"; 将传入的函数id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的函数返回值类型赋值给将当前记录的type; 将传入的函数参数个数赋值给当前记录的amount; 将传入的指向该函数子符号表指针赋值给当前记录的subSymbolTable; }
1.1.7.10 设置为主/子程序名方法
- 接口
setProgramName(string id, int lineNumber, string subprogramType, int amount, string returnType)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | program的id | | int lineNumber | program所在行号 | | string subprogramType | 该program的类型,只有Function和Procedure两种 | | int amount | 该program的参数个数 | | string returnType | 该program的返回值类型,若为Procedure,其返回值类型为void |
函数功能
设置当前program的程序名所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setProgramName(string id, int lineNumber, string subprogramType, int amount, string returnType) { 将当前记录的flag设为"(sub)program name"; 将传入的主程序名id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; 将传入的程序类型赋值给将当前记录的type; 将传入的程序参数个数赋值给当前记录的amount; 将传入的该程序返回值的类型赋值给当前记录的returnType; }
1.1.7.11 设置为主程序参数方法
- 接口
setVoidPara(string id, int lineNumber)
返回值
void
参数列表 | 参数 | 描述 | | —- | —- | | string id | 主程序参数的id | | int lineNumber | 主程序参数所在行号 |
函数功能
设置主程序参数所在记录的具体信息
- 函数伪代码
void _SymbolRecord::setVoidPara(string id, int lineNumber) { 将当前记录的flag设为"parameter of program"; 将传入的主程序参数id赋值给将当前记录的id; 将传入的行号赋值给将当前记录的lineNumber; }
1.1.8 对外接口
1.1.8.1 查找所在符号表位置
- 接口
_SymbolRecord findSymbolRecord(_SymbolTable currentSymbolTable, string id, int mode)
- 返回值
_SymbolRecord* findSymbolRecord
参数列表 | 参数 | 描述 | | —- | —- | | _SymbolTable* currentSymbolTable | 指向当前符号表的指针 | | string id | 标识符id | | int mode | 判断当前符号表是否为主符号表 |
函数功能
找出标识符在符号表中的位置
- 函数伪代码
1.1.8.2 检查是否与库程序名、主程序名、主程序参数同名
- 接口
bool checkIsTheSameAsKey(string id, int lineNumber)
- 返回值
bool
参数列表 | 参数 | 描述 | | —- | —- | | string id | 标识符id | | int lineNumber | 标识符行号 |
函数功能
检查标识符是否与库程序名、主程序名、主程序参数同名
- 函数伪代码
bool checkIsTheSameAsKey(string id, int lineNumber) { 遍历主符号表的第0个到第amount+4个记录 { if (标识符与记录中的id同名) { if (与主程序名同名) 调用addGeneralErrorInformation函数添加通用错误信息; else if (主程序参数同名) 调用addGeneralErrorInformation函数添加通用错误信息; else 调用addGeneralErrorInformation函数添加通用错误信息; return true; } } return false; }
1.1.8.3 找到第X个形式参数的类型
- 接口
string findXthFormalParaType(int X)
返回值
string
参数列表 | 参数 | 描述 | | —- | —- | | int X | 第X维形式参数 |
函数功能
找到第X维形式参数的类型
函数伪代码
string _SymbolRecord::findXthFormalParaType(int X) { 检查当前flag是否为"function"或者"procedure",并且传入的维度值未越界; 若是,则返回recordList第x个元素的type值; 若不是,则该记录不是"function"也不是"procedure",或者是维度值越界; }
1.1.8.4 检查第X个形式参数是否是引用调用
接口
bool isXthFormalParaRefered(int X)
返回值
bool
参数列表 | 参数 | 描述 | | —- | —- | | int X | 第X维形式参数 |
函数功能
检查第X维形式参数是否是引用调用
函数伪代码
bool _SymbolRecord::isXthFormalParaRefered(int X) { 检查当前flag是否为"function"或者"procedure",并且传入的维度值未越界; 若是,则检查返回recordList第x个元素的flag值是否为"var parameter", 若是,则返回ture; 若不是,则返回false; 若不是,则该记录不是"function"也不是"procedure",或者是维度值越界; }
1.1.8.5 检查第X维下标是否越界
接口
bool checkArrayXthIndexRange(int X,int index)
- 返回值
bool
参数列表 | 参数 | 描述 | | —- | —- | | int X | 第X维形式参数 | | int index | 第X个形式参数的下标 |
函数功能
检查第X维下标是否越界,true表示越界,false表示未越界
函数伪代码
bool _SymbolRecord::isXthFormalParaRefered(int X) { 检查当前flag是否为"array",并且传入的维度值未越界; 若是,则检查第x个元素的下标值是否大于数组下界并且小于数组上界, 若两者都符合,返回ture; 否则,返回false; 若不是,则该记录不是"array",或者是维度值越界; }
1.1.8.6 检查id是否是引用参数
接口
bool checkIsReferedPara(_SymbolTable* currentSymbolTable, string id)
- 返回值
bool
参数列表 | 参数 | 描述 | | —- | —- | | _SymbolTable* currentSymbolTable | 指向当前符号表的指针 | | string id | 标识符id |
函数功能
检查检查id是否是引用参数
- 函数伪代码
bool checkIsReferedPara(_SymbolTable* currentSymbolTable, string id) { if (当前符号表为主符号表) return false; if (当前符号表的id列表有值) { 根据id列表的下标找到记录在符号表中的下标; 从符号表中找到该记录; return 该记录的类型赋值为"var parameter"; } else { cout << "[checkIsReferedPara] id not found" << endl; } }
1.1.8.7 符号表指针
_SymbolTable mainSymbolTable;
指向主符号表的指针;
extern _SymbolTable currentSymbolTable;
指向当前符号表的指针。1.1.9 符号表定位与重定位
1.1.9.1 定位
在进入到抽象语法树每个子程序的定义节点时,创建子符号表,将程序名、参数个数、子符号表指针等信息保存到主符号表后,再定位到子符号表,继续进行程序定义部分的语义分析。1.1.9.2 重定位
在退出抽象语法树某个子程序的定义节点时,需要重定位到主符号表。