0%

编译原理-Lab2

第一个任务

用不同的样例覆盖语法规则,进行语义分析,遍历对应的 AST 树,发现语义的错误并输出到屏幕

建立符号表,并在合适的分析位置上进行输出符号表

需要特别注意以下两个问题:

  • 符号表的管理:
    • 符号表可以采用多种数据结构实现:顺序表,哈希表,十字链表,多表结构
    • 符号表上的操作包括创建符号表、插入表项、查询表项、修改表项、删除表项、释放符号表空间等等
  • 静态语义分析:
    • 控制流检查:控制流语句必须使得程序跳转到合法的地方
    • 唯一性检查:检查某些不能重复定义的对象或者元素(例如:同一作用域的标识符不能同名)
    • 名字的上下文相关性检查:名字的出现在遵循作用域与可见性的前提下应该满足一定的上下文的相关性
    • 类型检查:包括检查函数参数传递过程中形参与实参类型是否匹配,是否进行自动类型转换等

遍历 AST 树

AST 的遍历采用的是前序遍历(根左右),在遍历过程中:

  • 访问到了说明部分的结点时,在符号表中添加新的内容
  • 访问到执行语句部分时,根据访问的变量(或函数)名称查询符号表,并分析其静态语义的正确性

在上一个实验的 display 函数中已经实现了遍历的过程,AST 的节点结构体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct node { // 以下对结点属性定义没有考虑存储效率,只是简单地列出要用到的一些属性
enum node_kind kind; // 结点类型
union {
char type_id[33]; // 由标识符生成的叶结点
int type_int; // 由整常数生成的叶结点
char type_char; // 由字符型生成的叶节点
float type_float; // 由浮点常数生成的叶结点
};
struct node *ptr[3]; // 子树指针,由kind确定有多少棵子树
int level; // 层号
int place; // 表示结点对应的变量或运算结果临时变量在符号表的位置序号
char Etrue[15],Efalse[15]; // 对布尔表达式的翻译时,真假转移目标的标号
char Snext[15]; // 该结点对应语句执行后的下一条语句位置标号
struct codenode *code; // 该结点中间代码链表头指针
char op[10];
int type; // 结点对应值的类型
int pos; // 语法单位所在位置行号
int offset; // 偏移量
int width; // 各种数据占用的字节数
};

符号表

用结构体 symbol 来组织符号表信息:

1
2
3
4
5
6
7
8
9
typedef struct symbol {
char name[33]; // 变量或函数名
int level; // 层号,外部变量名或函数名层号为0,形参名为1,每到1个复合语句层号加1,退出减1
int type; // 变量类型或函数返回值类型
int paramnum; // 形式参数个数
char alias[10]; // 别名,为解决嵌套层次使用,使得每一个数据名称唯一
char flag; // 符号标记,函数:'F',变量:'V',参数:'P',临时变量:'T'
char offset; // 外部变量和局部变量在其静态数据区或活动记录中的偏移量,或函数活动记录大小,目标代码生成时使用
};

管理符号表的核心数据结构为顺序栈:

1
2
3
4
typedef struct symboltable{
struct symbol symbols[MAXLENGTH];
int index; // index初值为0
}SYMBOLTABLE;
  • 当遇到复合语句 COMP_STM 时,symboltable.symbols 会记录该作用域中的局部变量
  • 当退出复合语句 COMP_STM 时,可以通过修改 symboltable.index 来忽略局部变量
  • 因此需要一个格外的栈来记录程序进入 COMP_STM 前的 symboltable.index
1
2
3
4
typedef struct symbol_scope_begin {
int TX[30];
int top;
}SYMBOL_SCOPE_TX;
  • 当遇到复合语句 COMP_STM 时,symboltable.index 压栈
  • 当退出复合语句 COMP_STM 时,symboltable.index 弹出

显示符号表的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void DisplaySymbolTable()
{
int i;
printf("\t\t***Symbol Table***\n");
printf("---------------------------------------------------\n");
printf("%s\t%s\t%s\t%s\t%s\t%s\n","Index","Name","Level",
"Type","Flag","Param_num");
printf("---------------------------------------------------\n");
for(i=0;i<new_table.index;i++){
printf("%d\t",i);
printf("%s\t",new_table.symbols[i].name);
printf("%d\t",new_table.symbols[i].level);
if(new_table.symbols[i].type==INT)
printf("%s\t","int");
else if(new_table.symbols[i].type==FLOAT)
printf("%s\t","float");
else
printf("%s\t","char");
printf("%c\t",new_table.symbols[i].flag);
if(new_table.symbols[i].flag=='F')
printf("%d\n",new_table.symbols[i].paramnum);
else
printf("\n");
}
printf("---------------------------------------------------\n");
printf("\n");
}

作用域

在语义分析过程中,各个变量名有其对应的作用域,并且一个作用域内不允许名字重复

  • 通过层号 level 来管理,level 的初始值为 “0”
  • 在处理外部变量名以及函数名时,对应符号的层号值固定为 “0”
  • 处理函数形式参数时,固定形参名在填写符号表时,层号值固定为 1”
  • 每到一个复合语句,则层号 level 加“1”,退出时减“1”

通过 symboltablesymbol_scope_begin 这两个栈结构的配合,就可以实现作用域的变量区分

具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
case COMP_STM:
flag='T';
command=0;
new_scope.TX[new_scope.top]=new_table.index; /* index压栈 */
new_scope.top++;
Semantic_Analysis(T->ptr[0],type,level,flag,command); // 分析定义列表
command=1;
Semantic_Analysis(T->ptr[1],type,level+1,flag,command); // 分析语句列表
DisplaySymbolTable(); /* 显示符号表 */
new_table.index=new_scope.TX[new_scope.top-1]; /* index弹出 */
new_scope.top--;
if (new_scope.top == 0)
DisplaySymbolTable(); /* 显示符号表 */
  • 分析定义列表和语句列表时会向 symboltable 添加新的条目,同时进行报错分析(检查是否重复,是否合法等)
  • 在分析完成之后 symboltable.index 会进行更新,此时 symboltable 中新添加的条目会被弃用
  • 这样处理之后,同级语句块之间的分析过程不会相互干扰(父层还是会影响子层的分析)

定义与使用

核心函数的声明如下:

1
int Semantic_Analysis(struct node* T,int type,int level,char flag,int command)
  • 其中最后一个参数 command 就是用来决定是“定义”还是“使用”

通过上述代码也可以看出来:

1
2
3
4
5
6
7
8
case COMP_STM:
flag='T';
command=0;
.....
Semantic_Analysis(T->ptr[0],type,level,flag,command); // 分析定义列表
command=1;
Semantic_Analysis(T->ptr[1],type,level+1,flag,command); // 分析语句列表
.....
  • 在一个语句块中:
    • 前面所有的变量都会被当做“定义”
    • 后面所有的变量都会被当做“使用”
  • 这样写的话会有一些问题,在如下代码中:
1
2
int a1=1;
int a2=a1;
  • 程序会把 a1 当成“定义”,从而去遍历符号表造成冲突
  • 如果对赋值语句 ASSIGNOP 进行特殊处理又会出现其他的 BUG(主要是因为该程序采用递归的方法来遍历 AST,如果一个地方特殊处理,会导致很多地方发生连锁反应)

报错分析

常见错误类型如下:

  • 变量在使用时未定义
  • 函数在调用时为经定义
  • 变量出现重复定义或变量域前面定义过的其它语法结构重名
  • 函数出现重复定义
  • 赋值号两边的表达式类型不匹配
  • 赋值号左边只有一个右值的表达式(包括:赋值号右边无值)
  • 操作数类型不匹配或操作数类型域操作符不撇皮(例如:整形变量域数组变量相加减)
  • return 语句的返回类型域函数定义的返回类型不匹配
  • 函数调用时实参或形参的数目或类型不匹配

大概可以分为两类:重复定义,使用时未定义,类型不匹配

判断前两点都比较好实现:

  • 重复定义:我们只需要在新的符号进入符号表 symboltable 之前,先判断它是否已经存在于符号表中
  • 使用时未定义:在使用某个符号时,先遍历符号表 symboltable 看看它是否存于其中

详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
case ID: // 检测新的变量名是否唯一
i=0;
while(new_table.symbols[i].level!=level&&i<new_table.index) // 转到相同作用域
i++;
if(command==0){ // 定义变量
while(i<new_table.index){ /* 遍历并判断是否相同 */
if(strcmp(new_table.symbols[i].name,T->type_id)==0 && new_table.symbols[i].flag==flag){
if(flag=='V')
printf("ERROR!第%d行:全局变量中出现相同变量名%s\n",T->pos,T->type_id);
else if(flag=='F')
printf("ERROR!第%d行:函数定义中出现了相同的函数名%s\n",T->pos,T->type_id);
else if(flag=='T')
printf("ERROR!第%d行:局部变量中出现了相同的变量名%s\n",T->pos,T->type_id);
else
printf("ERROR!第%d行:函数参数中中出现了相同的变量名%s\n",T->pos,T->type_id);
return 0;
}
i++;
} /* 若不同则可以加入 */
strcpy(new_table.symbols[new_table.index].name,T->type_id);
new_table.symbols[new_table.index].level=level;
new_table.symbols[new_table.index].type=type;
new_table.symbols[new_table.index].flag=flag;
new_table.index++;
return new_table.symbols[i].type; /* 这里需要返回类型(类型匹配时有用) */
}
else{ // 使用变量
i=new_table.index-1;
while(i>=0){ /* 遍历并判断是否出现 */
if(strcmp(new_table.symbols[i].name,T->type_id)==0&&
(new_table.symbols[i].flag=='V'||
new_table.symbols[i].flag=='T'||
new_table.symbols[i].flag=='P')){
return new_table.symbols[i].type;
}
i--;
}
if(i<0){
printf("ERROR!第%d行:变量名%s未定义\n",T->pos,T->type_id);
}
}
break;
  • 对于函数的定义和调用,同样也需要遍历符号表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case FUNC_CALL:
j=0;
while(new_table.symbols[j].level==0&&j<new_table.index){
if(strcmp(new_table.symbols[j].name,T->type_id)==0){
if(new_table.symbols[j].flag!='F')
printf("ERROR!第%d行:函数名%s在符号表中定义为变量\n",T->pos,T->type_id);
break;
}
j++;
}
if(new_table.symbols[j].level==1||j==new_table.index){
printf("ERROR!第%d行:函数%s未定义\n",T->pos,T->type_id);
break;
}
type=new_table.symbols[j+1].type;
counter=0;
Semantic_Analysis(T->ptr[0],type,level,flag,command); // 分析参数
if(new_table.symbols[j].paramnum!=counter)
printf("ERROR!第%d行:函数调用%s参数个数不匹配\n",T->pos,T->type_id);
return new_table.symbols[j].type;
break;
  • 最后注意返回该函数的返回类型(为了后续的类型匹配)

类型不匹配则要分为以下几种情况:

  • 赋值语句类型不匹配(暂时不考虑自动类型转换)
  • 函数调用的参数类型不匹配
  • 函数调用的返回值不匹配

详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case ASSIGNOP:
case OR:
case AND:
case RELOP:
case PLUS:
case MINUS:
case STAR:
case DIV:
case COMADD:
case COMSUB:
type1=Semantic_Analysis(T->ptr[0],type,level,flag,command);
type2=Semantic_Analysis(T->ptr[1],type,level,flag,command);
if(type1==type2)
return type1;
else
printf("ERROR!第%d行:赋值类型不匹配\n",T->pos);
break;
  • 这里有个小细节需要注意:int a=1;int a; a=1; 是不同的
  • 第一种情况是函数 Semantic_Analysis 执行时,a 没有在符号表中
  • 第二种情况是函数 Semantic_Analysis 执行时,a 已经在符号表中
  • 对于这两种情况都要返回 a 的类型 new_table.symbols[i].type
1
2
3
4
5
6
7
8
case ARGS:
counter++;
t=Semantic_Analysis(T->ptr[0],type,level,flag,command);
if(type!=t)
printf("ERROR!第%d行:函数调用的第%d个参数类型不匹配\n",T->pos,counter);
type=new_table.symbols[j+counter+1].type;
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
  • 函数参数的类型匹配需要借助全局变量 counter 来定位

符号表构建

接下来的工作就是针对不同的节点类型来执行对应的操作,主要还是通过递归来遍历 AST 中的各个节点

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
int Semantic_Analysis(struct node* T,int type,int level,char flag,int command)
{
int type1,type2;
if(T){
switch(T->kind){
case EXT_DEF_LIST:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case EXT_VAR_DEF://外部变量声明
type=Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case ARRAY_DEF:
type = Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case ARRAY_DEC:
flag = 'A';//Array
strcpy(new_table.symbols[new_table.index].name,T->type_id);
new_table.symbols[new_table.index].level=level;
new_table.symbols[new_table.index].type=type;
new_table.symbols[new_table.index].flag=flag;
new_table.index++;
break;
case TYPE:
return T->type;
case EXT_DEC_LIST:
flag='V';
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case ID://检测新的变量名是否唯一
i=0;
while(new_table.symbols[i].level!=level&&i<new_table.index)//转到相同作用域
i++;
if(command==0){//定义变量
while(i<new_table.index){
if(strcmp(new_table.symbols[i].name,T->type_id)==0 && new_table.symbols[i].flag==flag){
if(flag=='V')
printf("ERROR!第%d行:全局变量中出现相同变量名%s\n",T->pos,T->type_id);
else if(flag=='F')
printf("ERROR!第%d行:函数定义中出现了相同的函数名%s\n",T->pos,T->type_id);
else if(flag=='T')
printf("ERROR!第%d行:局部变量中出现了相同的变量名%s\n",T->pos,T->type_id);
else
printf("ERROR!第%d行:函数参数中中出现了相同的变量名%s\n",T->pos,T->type_id);
return 0;
}
i++;
}
strcpy(new_table.symbols[new_table.index].name,T->type_id);
new_table.symbols[new_table.index].level=level;
new_table.symbols[new_table.index].type=type;
new_table.symbols[new_table.index].flag=flag;
new_table.index++;
return new_table.symbols[i].type;
}
else{//使用变量
i=new_table.index-1;
while(i>=0){
if(strcmp(new_table.symbols[i].name,T->type_id)==0&&(new_table.symbols[i].flag=='V'||new_table.symbols[i].flag=='T'||new_table.symbols[i].flag=='P')){
return new_table.symbols[i].type;
}
i--;
}
if(i<0){
printf("ERROR!第%d行:变量名%s未定义\n",T->pos,T->type_id);
}
}
break;
case FUNC_DEF://函数声明
type=Semantic_Analysis(T->ptr[0],type,level+1,flag,command);
Semantic_Analysis(T->ptr[1],type,1,flag,command);
Semantic_Analysis(T->ptr[2],type,1,flag,command);
break;
case FUNC_DEC:
strcpy(new_table.symbols[new_table.index].name,T->type_id);
new_table.symbols[new_table.index].level=0;
new_table.symbols[new_table.index].type=type;
new_table.symbols[new_table.index].flag='F';
new_table.index++;
counter=0;
Semantic_Analysis(T->ptr[0],type,level,flag,command);//函数形参
new_table.symbols[new_table.index - counter - 1].paramnum=counter;
break;
case PARAM_LIST:
counter++;
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case PARAM_DEC:
flag='P';
type=Semantic_Analysis(T->ptr[0],type,level+1,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case COMP_STM:
flag='T';
command=0;
new_scope.TX[new_scope.top]=new_table.index;
new_scope.top++;
Semantic_Analysis(T->ptr[0],type,level,flag,command);//分析定义列表
command=1;
Semantic_Analysis(T->ptr[1],type,level+1,flag,command);//分析语句列表
DisplaySymbolTable();
new_table.index=new_scope.TX[new_scope.top-1];
new_scope.top--;
if (new_scope.top == 0)
DisplaySymbolTable();
break;
case DEF_LIST:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case VAR_DEF:
type=Semantic_Analysis(T->ptr[0],type,level+1,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case DEC_LIST:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case STM_LIST:
Semantic_Analysis(T->ptr[0],type,level,flag,command);//第一个语句
Semantic_Analysis(T->ptr[1],type,level,flag,command);//其他语句
break;
case EXP_STMT:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
break;
case RETURN:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
break;
case IF_THEN:
case WHILE:
case FOR:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
case IF_THEN_ELSE:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
Semantic_Analysis(T->ptr[1],type,level,flag,command);
Semantic_Analysis(T->ptr[2],type,level,flag,command);
break;
case ASSIGNOP:
case OR:
case AND:
case RELOP:
case PLUS:
case MINUS:
case STAR:
case DIV:
case COMADD:
case COMSUB:
type1=Semantic_Analysis(T->ptr[0],type,level,flag,command);
type2=Semantic_Analysis(T->ptr[1],type,level,flag,command);
if(type1==type2)
return type1;
else
printf("ERROR!第%d行:赋值类型不匹配\n",T->pos);
break;
case AUTOADD_L:
case AUTOSUB_L:
case AUTOADD_R:
case AUTOSUB_R:
Semantic_Analysis(T->ptr[0],type,level,flag,command);
break;
case INT:
return INT;
case FLOAT:
return FLOAT;
case CHAR:
return CHAR;
case FUNC_CALL:
j=0;
while(new_table.symbols[j].level==0&&j<new_table.index){
if(strcmp(new_table.symbols[j].name,T->type_id)==0){
if(new_table.symbols[j].flag!='F')
printf("ERROR!第%d行:函数名%s在符号表中定义为变量\n",T->pos,T->type_id);
break;
}
j++;
}
if(new_table.symbols[j].level==1||j==new_table.index){
printf("ERROR!第%d行:函数%s未定义\n",T->pos,T->type_id);
break;
}
type=new_table.symbols[j+1].type;
counter=0;
Semantic_Analysis(T->ptr[0],type,level,flag,command);//分析参数
if(new_table.symbols[j].paramnum!=counter)
printf("ERROR!第%d行:函数调用%s参数个数不匹配\n",T->pos,T->type_id);
return new_table.symbols[j].type;
break;
case ARGS:
counter++;
t=Semantic_Analysis(T->ptr[0],type,level,flag,command);
if(type!=t)
printf("ERROR!第%d行:函数调用的第%d个参数类型不匹配\n",T->pos,counter);
type=new_table.symbols[j+counter+1].type;
Semantic_Analysis(T->ptr[1],type,level,flag,command);
break;
}
}
return 0;
}

编译与结果

1
2
3
flex lexer.l
bison -d -v parser.y
gcc display.c parser.tab.c lex.yy.c -lfl -o test1

测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int a,b,c;
float m,n;
char c1,c2;
char h[10];

float a,b;//全局变量中出现相同变量名

int fibo(int a)
{
int i;
int haha;
if(a == 1 || a == 2){
return 1;
}
for(i<15){
i++;
}

j = i+1;//无定义错误
haha(c);//未定义的函数
a = fibo(1,2);//参数个数不匹配
b = fibo(m);//参数类型不匹配

return fibo(a-1)+fibo(a-2);
}

int h1(int a, int a){}//出现了相同函数参数

int h2(){int hah; float hah;}//局部变量名出现了相同的变量名

float h1(){}//重复的函数名

最终结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
 外部变量定义:
类型:int
变量名:
ID: a
ID: b
ID: c
外部变量定义:
类型:float
变量名:
ID: m
ID: n
外部变量定义:
类型:char
变量名:
ID: c1
ID: c2
数组定义:
类型:char
数组名:h
数组大小:
INT: 10
外部变量定义:
类型:float
变量名:
ID: a
ID: b
函数定义:
类型:int
函数名:fibo
函数型参:
类型:int
ID: a
复合语句:
复合语句的变量定义:
局部变量名:
类型:int
变量名:
ID: i
局部变量名:
类型:int
变量名:
ID: haha
复合语句的语句部分:
条件语句(if-else):
条件:
OR
==
ID: a
INT: 1
==
ID: a
INT: 2
IF语句:
复合语句:
复合语句的变量定义:
复合语句的语句部分:
返回语句:
INT: 1
循环语句(for):
循环条件:
<
ID: i
INT: 15
循环体:
复合语句:
复合语句的变量定义:
复合语句的语句部分:
表达式语句:
AUTOADD
ID: i
表达式语句:
ASSIGNOP
ID: j
PLUS
ID: i
INT: 1
表达式语句:
函数调用:
函数名:haha
第一个实际参数表达式:
ID: c
表达式语句:
ASSIGNOP
ID: a
函数调用:
函数名:fibo
第一个实际参数表达式:
INT: 1
INT: 2
表达式语句:
ASSIGNOP
ID: b
函数调用:
函数名:fibo
第一个实际参数表达式:
ID: m
返回语句:
PLUS
函数调用:
函数名:fibo
第一个实际参数表达式:
MINUS
ID: a
INT: 1
函数调用:
函数名:fibo
第一个实际参数表达式:
MINUS
ID: a
INT: 2
函数定义:
类型:int
函数名:h1
函数型参:
类型:int
ID: a
类型:int
ID: a
复合语句:
复合语句的变量定义:
复合语句的语句部分:
函数定义:
类型:int
函数名:h2
函数型参:
复合语句:
复合语句的变量定义:
局部变量名:
类型:int
变量名:
ID: hah
局部变量名:
类型:float
变量名:
ID: hah
复合语句的语句部分:
函数定义:
类型:float
函数名:h1
函数型参:
复合语句:
复合语句的变量定义:
复合语句的语句部分:
ERROR!第6行:全局变量中出现相同变量名a
ERROR!第6行:全局变量中出现相同变量名b
***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 1
9 a 1 int P
10 i 1 int T
11 haha 1 int T
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 1
9 a 1 int P
10 i 1 int T
11 haha 1 int T
---------------------------------------------------

ERROR!第21行:变量名j未定义
ERROR!第21行:赋值类型不匹配
ERROR!第22行:函数haha未定义
ERROR!第23行:函数调用fibo参数个数不匹配
ERROR!第23行:赋值类型不匹配
ERROR!第24行:函数调用的第1个参数类型不匹配
ERROR!第24行:赋值类型不匹配
***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 1
9 a 1 int P
10 i 1 int T
11 haha 1 int T
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 1
9 a 1 int P
---------------------------------------------------

ERROR!第29行:函数参数中中出现了相同的变量名a
ERROR!第29行:函数参数中中出现了相同的变量名a
***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
---------------------------------------------------

ERROR!第31行:局部变量中出现了相同的变量名hah
***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
11 h2 0 int F 0
12 hah 1 int T
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
11 h2 0 int F 0
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
11 h2 0 int F 0
12 h1 0 float F 0
---------------------------------------------------

***Symbol Table***
---------------------------------------------------
Index Name Level Type Flag Param_num
---------------------------------------------------
0 a 0 int V
1 b 0 int V
2 c 0 int V
3 m 0 float V
4 n 0 float V
5 c1 0 char V
6 c2 0 char V
7 h 0 char A
8 fibo 0 int F 2
9 a 1 int P
10 h1 0 int F 0
11 h2 0 int F 0
12 h1 0 float F 0
---------------------------------------------------