第一个任务 通过前面对 AST 遍历,完成了语义分析后,如果没有语法语义错误,就可以再次对 AST 进行遍历,计算相关的属性值,建立符号表,并生成以三地址代码 TAC 作为中间语言的中间语言代码序列
在这里对本实验的实现做了一些限制,假设数据类型只包含整数类型,不包含如浮点数、数组、结构和指针等其它数据类型
实验目标:完成语义分析的下半部分,以根据 AST 生成中间语言 TAC
三地址代码 TAC
中间语言(中间代码)是一种面向语法,易于翻译成目标程序的等效内部表示代码(二进制码)
其可理解性及易于生成目标代码的程度介于源语言和目标语言之间
三地址中间代码 TAC 是一个4元组,逻辑上包含 (op、opn1、opn2、result)
:
op:表示操作类型说明
opn1 和 opn2:表示2个操作数
result:表示运算结果
后续还需要根据 TAC 序列生成目标代码,所以设计其存储结构时,每一部分要考虑目标代码生成时所需要的信息
表 4-1 中间代码定义:
语法
描述
Op
Opn1
Opn2
Result
LABEL x
定义标号 x
LABEL
X
FUNCTION f:
定义函数 f
FUNCTION
F
x := y
赋值操作
ASSIGN
X
X
x := y + z
加法操作
PLUS
Y
Z
X
x := y - z
减法操作
MINUS
Y
Z
X
x := y * z
乘法操作
STAR
Y
Z
X
x := y / z
除法操作
DIV
Y
Z
X
GOTO x
无条件转移
GOTO
X
IF x [relop] y GOTO z
条件转移
[relop]
X
Y
Z
RETURN x
返回语句
RETURN
X
ARG x
传实参 x
ARG
X
x:=CALL f
调用函数
CALL
F
X
PARAM x
函数形参
PARAM
X
READ x
读入
READ
X
WRITE x
打印
WRITE
X
运算符:
表示这条指令需要完成的运算,可以用枚举常量表示,如 PLUS 表示双目加,JLE 表示小于等于,PARAM 表示形参,ARG 表示实参等
操作数与运算结果:
这些部分包含的数据类型有多种,整常量,实常量,还有使用标识符的情况,如变量的别名、变量在其数据区的偏移量和层号、转移语句中的标号等
类型不同,所以考虑使用联合,为了明确联合中的有效成员,将操作数与运算结果设计成结构类型,包含 kind,联合等几个成员,kind 说明联合中的有效,联合成员是整常量,实常量或标识符表示的别名或标号或函数名等
为了配合后续的 TAC 代码序列的生成,将 TAC 代码作为数据元素,用双向循环链表表示 TAC 代码序列
翻译模式
翻译模式:把语义规则(也称为语义动作),用花括号 {}
括起来,插入到产生式右部的合适位置上,进一步细化了语义计算的时机
翻译模式用于指明各个符号的具体含义,当 “词法分析” 和 “语法分析” 都执行完毕以后,程序便会开始 “语义分析”,其大概包含以下两个步骤:
生成符号表,并进行:重复检查,未声明/定义检查,类型匹配检查
为 AST 的各个节点类型编写“含义”(根据翻译模式对各个节点的意义进行解释)
翻译模式共有两种:
S-翻译模式:
只涉及到综合属性,语义动作集置于产生式右端的末尾
常采用 LR 的自底向上的分析法,和S-属性文法类似
L-翻译模式:
包含综合属性,也可以包含继承属性
必须满足以下条件:
产生式右部符号的继承属性,其语义计算必须位于该符号前,且语义动作不访问右边符号的属性
产生式左部符号的综合属性只有在它所引用的所有属性都计算出来之后才可以计算,计算这种属性的动作通常放在产生式的末尾
继承属性只能依赖于兄长的属性、父亲的继承属性、自身的属性
属性间的依赖图不能形成环路
本实验将会采用S-翻译模式和 LR(1) 文法:
基础文法 LL(1):自顶向下计算
基础文法 LR(1):自底向上计算
属性计算
在遍历过程中,需要根据翻译模式给出的计算方法完成属性的计算,本实验中设置的属性如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct node { ...... 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; int num; };
.place :记录该结点操作数在符号表中的位置序号,每次完成了计算后,中间结果需要用一个临时变量保存,临时变量也需要登记到符号表中(返回局部变量会生成一个临时变量,即没有名字的变量)
.type :记录数据的类型,用于表达式的计算中,该属性也可用于语句,表示语句语义分析的正确性(OK或ERROR)
.offset :记录外部变量在静态数据区中的偏移量,以及局部变量和临时变量在活动记录中的偏移量(另外对函数,这项保存活动记录的大小)
.width :记录一个结点表示的语法单位中,定义的变量和临时单元所需要占用的字节数,方便计算变量、临时变量在活动记录中偏移量,以及最后计算函数活动记录的大小
.code :记录中间代码序列的起始位置,如采用链表表示中间代码序列,该属性就是一个链表的头指针
.Etrue 和 .Efalse :在完成布尔表达式翻译时,表达式值为真假时要转移的程序位置(标号的字符串形式)
.Snext :该结点的语句序列执行完后,要转移的程序位置(标号的字符串形式)
这一步非常复杂,如果有必要的话,我们需要对每个类型的节点都设置一个函数,用于解析它本身的语义:
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 void semantic_Analysis (struct node *T) { if (T) { switch (T->kind) { case EXT_DEF_LIST: ext_def_list(T); break ; case EXT_VAR_DEF: ext_var_def(T); break ; case FUNC_DEF: func_def(T); break ; case FUNC_DEC: func_dec(T); break ; case PARAM_LIST: param_list(T); break ; case PARAM_DEC: param_dec(T); break ; case COMP_STM: comp_stm(T); break ; case DEF_LIST: def_list(T); break ; case VAR_DEF: var_def(T); break ; case STM_LIST: stmt_list(T); break ; case IF_THEN: if_then(T); break ; case IF_THEN_ELSE: if_then_else(T); break ; case WHILE: while_dec(T); break ; case FOR: for_dec(T); break ; case FOR_DEC: for_list(T); break ; case EXP_STMT: exp_stmt(T); break ; case RETURN: return_dec(T); break ; case ID: case STRUCT_TAG: case INT: case FLOAT: case CHAR: case STRING: case ASSIGNOP: case AND: case OR: case RELOP: case PLUS: case PPLUS: case PLUSASSIGNOP: case MINUS: case MMINUS: case MINUSASSIGNOP: case STAR: case STARASSIGNOP: case DIV: case DIVASSIGNOP: case NOT: case UMINUS: case FUNC_CALL: case EXP_ARRAY: Exp(T); break ; } } }
上述展示的节点类型并不完整,有些类型的处理函数会出现在其子函数中
这里要补充一下:和上个实验相比,新填了3个节点:
1 2 3 4 5 OptTag : {$$=NULL ;} | ID {$$=mknode(STRUCT_TAG,NULL ,NULL ,NULL ,yylineno);strcpy ($$->struct_name,$1 );} ; Tag : ID {$$=mknode(STRUCT_TAG,NULL ,NULL ,NULL ,yylineno);strcpy ($$->struct_name,$1 );} ;
STRUCT_DEF:结构体定义
STRUCT_DEC:结构体声明
1 2 3 StructSpecifier : STRUCT OptTag LC DefList RC {$$=mknode(STRUCT_DEF,$2 ,$4 ,NULL ,yylineno);} | STRUCT Tag {$$=mknode(STRUCT_DEC,$2 ,NULL ,NULL ,yylineno);} ;
修改了1个节点:(另外几个只是改了名字,代码没有变)
1 2 3 4 5 6 7 8 Stmt: Exp SEMI {$$=mknode(EXP_STMT,$1 ,NULL ,NULL ,yylineno);} | CompSt {$$=$1 ;} | RETURN Exp SEMI {$$=mknode(RETURN,$2 ,NULL ,NULL ,yylineno);} | IF LP Exp RP Stmt %prec LOWER_THEN_ELSE {$$=mknode(IF_THEN,$3 ,$5 ,NULL ,yylineno);} | IF LP Exp RP Stmt ELSE Stmt {$$=mknode(IF_THEN_ELSE,$3 ,$5 ,$7 ,yylineno);} | WHILE LP Exp RP Stmt {$$=mknode(WHILE,$3 ,$5 ,NULL ,yylineno);} | FOR LP ForDec RP Stmt {$$=mknode(FOR,$3 ,$5 ,NULL ,yylineno);} ;
1 2 3 ForDec: Exp SEMI Exp SEMI Exp {$$=mknode(FOR_DEC,$1 ,$3 ,$5 ,yylineno);} | SEMI Exp SEMI {$$=mknode(FOR_DEC,NULL ,$2 ,NULL ,yylineno);} ;
中间代码的生成
核心结构体如下:
1 2 3 4 5 6 struct codenode { int op; struct opn opn1 , opn2 , result ; struct codenode *next , *prior ; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct opn { int kind; int type; union { int const_int; float const_float; char const_char; char *const_string; char id[33 ]; struct Array *type_array ; struct Struct *type_struct ; }; int level; int offset; };
中间代码生成的过程其实就是完成 codenode
双向链表,其中的 opn1, opn2, result
需要根据 “表 4-1 中间代码定义” 来进行填写
接下来就按照逻辑顺序,依次展示各个节点的中间代码的生成过程
全局变量定义 (本实验的外部变量就是全局变量)
EXT_DEF_LIST:外部定义列表(EXT_VAR_DEF | ARRAY_DEF | FUNC_DEF,EXT_DEF_LIST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void ext_def_list (struct node *T) { if (!T->ptr[0 ]) return ; T->ptr[0 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); T->code = T->ptr[0 ]->code; if (T->ptr[1 ]) { T->ptr[1 ]->offset = T->ptr[0 ]->offset + T->ptr[0 ]->width; semantic_Analysis(T->ptr[1 ]); T->code = merge(2 , T->code, T->ptr[1 ]->code); } }
函数 merge
:合并多个中间代码的双向循环链表,首尾相连
EXT_VAR_DEF:外部变量声明(TYPE,EXT_DEC_LIST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void ext_var_def (struct node *T) { if (!strcmp (T->ptr[0 ]->type_id, "int" )) { T->type = T->ptr[1 ]->type = INT; T->ptr[1 ]->width = 4 ; } if (!strcmp (T->ptr[0 ]->type_id, "float" )) { T->type = T->ptr[1 ]->type = FLOAT; T->ptr[1 ]->width = 8 ; } if (!strcmp (T->ptr[0 ]->type_id, "char" )) { T->type = T->ptr[1 ]->type = CHAR; T->ptr[1 ]->width = 1 ; } T->ptr[1 ]->offset = T->offset; ext_var_list(T->ptr[1 ]); T->width = (T->ptr[1 ]->width) * T->ptr[1 ]->num; T->code = NULL ; }
EXT_DEC_LIST 节点中可能记录了类型相同的多个变量声明,需要递归处理
假定全局变量不支持初始化,因此不需要设置中间代码 code
EXT_DEC_LIST:变量名称列表(ID,EXT_DEC_LIST)
ID:变量名称(ID)
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 ext_var_list (struct node *T) { int rtn, num = 1 ; switch (T->kind) { case EXT_DEC_LIST: T->ptr[0 ]->type = T->type; T->ptr[0 ]->offset = T->offset; T->ptr[1 ]->type = T->type; T->ptr[1 ]->offset = T->offset + T->width; T->ptr[1 ]->width = T->width; ext_var_list(T->ptr[0 ]); ext_var_list(T->ptr[1 ]); T->num = T->ptr[1 ]->num + 1 ; break ; case ID: rtn = fillSymbolTable(T->type_id, newAlias(), LEV, T->type, 'V' , T->offset); if (rtn == -1 ) semantic_error(T->pos, T->type_id, "变量重复定义,语义错误" ); else T->place = rtn; T->num = 1 ; break ; default : break ; } }
函数 fillSymbolTable
:根据 name
查符号表,不能重复定义:
重复定义,则返回 -1
不重复定义,则填表并返回符号在符号表中的位置序号
分析以上这3个函数的流程,就可以大概清楚属性计算的流程,在遍历 AST 的过程中就可以自下而上来分析节点属性
函数定义
FUNC_DEF:函数定义(TYPE,FUNC_DEC,COMP_STM)
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 void func_def (struct node *T) { if (!strcmp (T->ptr[0 ]->type_id, "int" )) { T->ptr[1 ]->type = INT; } if (!strcmp (T->ptr[0 ]->type_id, "float" )) { T->ptr[1 ]->type = FLOAT; } if (!strcmp (T->ptr[0 ]->type_id, "char" )) { T->ptr[1 ]->type = CHAR; } T->width = 0 ; T->offset = DX; semantic_Analysis(T->ptr[1 ]); T->offset += T->ptr[1 ]->width; T->ptr[2 ]->offset = T->offset; strcpy (T->ptr[2 ]->Snext, newLabel()); semantic_Analysis(T->ptr[2 ]); symbolTable.symbols[T->ptr[1 ]->place].offset = T->offset + T->ptr[2 ]->width; T->code = merge(3 , T->ptr[1 ]->code, T->ptr[2 ]->code, genLabel(T->ptr[2 ]->Snext)); }
函数 newLabel
:生成一个标号,标号命名形式为 “LABEL+序号”
函数 genLabel
:生成标号语句
FUNC_DEC:函数声明(PARAM_LIST)
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 void func_dec (struct node *T) { int rtn; struct opn opn1 , opn2 , result ; rtn = fillSymbolTable(T->type_id, newAlias(), LEV, T->type, 'F' , 0 ); if (rtn == -1 ) { semantic_error(T->pos, T->type_id, "函数名重复使用,可能是函数重复定义,语义错误" ); return ; } else T->place = rtn; result.kind = ID; strcpy (result.id, T->type_id); result.offset = rtn; T->code = genIR(FUNCTION, opn1, opn2, result); T->offset = DX; if (T->ptr[0 ]) { T->ptr[0 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); T->width = T->ptr[0 ]->width; symbolTable.symbols[rtn].paramnum = T->ptr[0 ]->num; T->code = merge(2 , T->code, T->ptr[0 ]->code); } else symbolTable.symbols[rtn].paramnum = 0 , T->width = 0 ; }
函数 genIR
:生成一条 TAC 的中间代码语句
一般情况下,TAC 中涉及到2个运算对象和运算结果
如果是局部变量或临时变量,表示在运行时,其对应的存储单元在活动记录中,这时需要将其偏移量 offset 和数据类型同时带上,方便最后的目标代码生成
全局变量也需要带上偏移量
查中间代码表可知 “FUNCTION 函数名” 不需要 opn1 opn2
,只需要 result
PARAM_LIST:参数定义列表(PARAM_DEC)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void param_list (struct node *T) { T->ptr[0 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); if (T->ptr[1 ]) { T->ptr[1 ]->offset = T->offset + T->ptr[0 ]->width; semantic_Analysis(T->ptr[1 ]); T->num = T->ptr[0 ]->num + T->ptr[1 ]->num; T->width = T->ptr[0 ]->width + T->ptr[1 ]->width; T->code = merge(2 , T->ptr[0 ]->code, T->ptr[1 ]->code); } else { T->num = T->ptr[0 ]->num; T->width = T->ptr[0 ]->width; T->code = T->ptr[0 ]->code; } }
PARAM_DEC:参数定义(TYPE,ID)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void param_dec (struct node *T) { int rtn; struct opn opn1 , opn2 , result ; rtn = fillSymbolTable(T->ptr[1 ]->type_id, newAlias(), 1 , T->ptr[0 ]->type, 'P' , T->offset); if (rtn == -1 ) semantic_error(T->ptr[1 ]->pos, T->ptr[1 ]->type_id, "参数名重复定义,语义错误" ); else T->ptr[1 ]->place = rtn; T->num = 1 ; T->width = T->ptr[0 ]->type == INT ? 4 : 8 ; result.kind = ID; strcpy (result.id, symbolTable.symbols[rtn].alias); result.offset = T->offset; T->code = genIR(PARAM, opn1, opn2, result); }
COMP_STM:复合语句(DEF_LIST,STM_LIST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void comp_stm (struct node *T) { LEV++; symbol_scope_TX.TX[symbol_scope_TX.top++] = symbolTable.index; T->width = 0 ; T->code = NULL ; if (T->ptr[0 ]) { T->ptr[0 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); T->width += T->ptr[0 ]->width; T->code = T->ptr[0 ]->code; } if (T->ptr[1 ]) { T->ptr[1 ]->offset = T->offset + T->width; strcpy (T->ptr[1 ]->Snext, T->Snext); semantic_Analysis(T->ptr[1 ]); T->width += T->ptr[1 ]->width; T->code = merge(2 , T->code, T->ptr[1 ]->code); } LEV--; symbolTable.index = symbol_scope_TX.TX[--symbol_scope_TX.top]; }
symbol_scope_TX
是一个存放 symbolTable.index
的栈结构
表达式
下面给一个通用表达式 Exp 的案例:
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 void Exp (struct node *T) { if (T) { switch (T->kind) { case ID: id_exp(T); break ; case CHAR: char_exp(T); break ; case INT: int_exp(T); break ; case FLOAT: float_exp(T); break ; case ASSIGNOP: assignop_exp(T); break ; case PPLUS: case MMINUS: oop_exp(T); break ; case AND: case OR: case RELOP: relop_exp(T); break ; case PLUSASSIGNOP: case MINUSASSIGNOP: case STARASSIGNOP: case DIVASSIGNOP: case PLUS: case MINUS: case STAR: case DIV: op_exp(T); break ; case FUNC_CALL: func_call_exp(T); break ; case ARGS: args_exp(T); break ; } } }
表达式 Exp 的类型很多,这里只选择几个简单分析:
ID:名称(NULL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void id_exp (struct node *T) { int rtn; rtn = searchSymbolTable(T->type_id); if (rtn == -1 ) semantic_error(T->pos, T->type_id, "变量未声明定义就引用,语义错误" ); if (symbolTable.symbols[rtn].flag == 'F' ) semantic_error(T->pos, T->type_id, "是函数名,不是普通变量,语义错误" ); else { T->place = rtn; T->code = NULL ; T->type = symbolTable.symbols[rtn].type; T->offset = symbolTable.symbols[rtn].offset; T->width = 0 ; } }
INT:int 类型(其他基础类型同理)
1 2 3 4 5 6 7 8 9 10 11 12 void int_exp (struct node *T) { struct opn opn1 , opn2 , result ; T->place = temp_add(newTemp(), LEV, T->type, 'T' , T->offset); T->type = INT; opn1.kind = INT; opn1.const_int = T->type_int; result.kind = ID; strcpy (result.id, symbolTable.symbols[T->place].alias); result.offset = symbolTable.symbols[T->place].offset; T->code = genIR(ASSIGNOP, opn1, opn2, result); T->width = 4 ; }
返回局部变量会生成一个临时变量,由于 int 和 float 会参与计算,因此需要一个临时变量来存储计算的结果
函数 temp_add
:填写临时变量到符号表,返回临时变量在符号表中的位置
ASSIGNOP:赋值(ID,Exp)
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 void assignop_exp (struct node *T) { struct opn opn1 , opn2 , result ; if (T->ptr[0 ]->kind != ID) { semantic_error(T->pos, "" , "赋值语句没有左值,语义错误" ); } else { Exp(T->ptr[0 ]); T->ptr[1 ]->offset = T->offset; Exp(T->ptr[1 ]); T->type = T->ptr[0 ]->type; T->width = T->ptr[1 ]->width; T->code = merge(2 , T->ptr[0 ]->code, T->ptr[1 ]->code); opn1.kind = ID; strcpy (opn1.id, symbolTable.symbols[T->ptr[1 ]->place].alias); opn1.offset = symbolTable.symbols[T->ptr[1 ]->place].offset; result.kind = ID; strcpy (result.id, symbolTable.symbols[T->ptr[0 ]->place].alias); result.offset = symbolTable.symbols[T->ptr[0 ]->place].offset; T->code = merge(2 , T->code, genIR(ASSIGNOP, opn1, opn2, result)); } }
FUNC_CALL:函数调用(ARGS)
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 void func_call_exp (struct node *T) { int rtn,width; struct node *T0 ; struct opn opn1 , opn2 , result ; rtn = searchSymbolTable(T->type_id); if (rtn == -1 ) { semantic_error(T->pos, T->type_id, "函数未定义,语义错误" ); return ; } if (symbolTable.symbols[rtn].flag != 'F' ) { semantic_error(T->pos, T->type_id, "不是函数名,不能进行函数调用,语义错误" ); return ; } T->type = symbolTable.symbols[rtn].type; width = T->type == INT ? 4 : 8 ; if (T->ptr[0 ]) { T->ptr[0 ]->offset = T->offset; Exp(T->ptr[0 ]); T->width = T->ptr[0 ]->width + width; T->code = T->ptr[0 ]->code; } else { T->width = width; T->code = NULL ; } match_param(rtn, T->ptr[0 ]); T0 = T->ptr[0 ]; while (T0) { result.kind = ID; strcpy (result.id, symbolTable.symbols[T0->ptr[0 ]->place].alias); result.offset = symbolTable.symbols[T0->ptr[0 ]->place].offset; T->code = merge(2 , T->code, genIR(ARG, opn1, opn2, result)); T0 = T0->ptr[1 ]; } T->place = temp_add(newTemp(), LEV, T->type, 'T' , T->offset + T->width - width); opn1.kind = ID; strcpy (opn1.id, T->type_id); opn1.offset = rtn; result.kind = ID; strcpy (result.id, symbolTable.symbols[T->place].alias); result.offset = symbolTable.symbols[T->place].offset; T->code = merge(2 , T->code, genIR(CALL, opn1, opn2, result)); }
生成函数调用中间代码
函数 match_param
:匹配函数参数
ARGS:函数参数(Exp | Exp,Args)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void args_exp (struct node *T) { T->ptr[0 ]->offset = T->offset; Exp(T->ptr[0 ]); T->width = T->ptr[0 ]->width; T->code = T->ptr[0 ]->code; if (T->ptr[1 ]) { T->ptr[1 ]->offset = T->offset + T->ptr[0 ]->width; Exp(T->ptr[1 ]); T->width += T->ptr[1 ]->width; T->code = merge(2 , T->code, T->ptr[1 ]->code); } }
语句列表
在开始分析选择/循环语句之前需要先分析:STM_LIST
STM_LIST:语句列表(Stmt-语句节点,STM_LIST)
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 void stmt_list (struct node *T) { if (!T->ptr[0 ]) { T->code = NULL ; T->width = 0 ; return ; } if (T->ptr[1 ]) strcpy (T->ptr[0 ]->Snext, newLabel()); else strcpy (T->ptr[0 ]->Snext, T->Snext); T->ptr[0 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); T->code = T->ptr[0 ]->code; T->width = T->ptr[0 ]->width; if (T->ptr[1 ]) { strcpy (T->ptr[1 ]->Snext, T->Snext); T->ptr[1 ]->offset = T->offset; semantic_Analysis(T->ptr[1 ]); if (T->ptr[0 ]->kind == RETURN || T->ptr[0 ]->kind == EXP_STMT || T->ptr[0 ]->kind == COMP_STM) T->code = merge(2 , T->code, T->ptr[1 ]->code); else T->code = merge(3 , T->code, genLabel(T->ptr[0 ]->Snext), T->ptr[1 ]->code); if (T->ptr[1 ]->width > T->width) T->width = T->ptr[1 ]->width; } }
函数 newLabel
会生成一个标号,用于记录 if 语句为真时跳转的位置
只要 STM_LIST 节点的 STM_LIST 子树存在,程序就会给 Stmt 子树分配一个标号
根据代码逻辑,在选择/循环语句后必定有一个 LABEL labeln
,用于标记跳转
SEMI:语句(Stmt-语句节点)
语句的类型很多,这里重点分析选择语句和循环语句
选择/循环语句
IF_THEN_ELSE:IF ELSE 语句(Exp,Stmt-语句节点,Stmt-语句节点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void if_then_else (struct node *T) { strcpy (T->ptr[0 ]->Etrue, newLabel()); strcpy (T->ptr[0 ]->Efalse, newLabel()); T->ptr[0 ]->offset = T->ptr[1 ]->offset = T->ptr[2 ]->offset = T->offset; boolExp(T->ptr[0 ]); T->width = T->ptr[0 ]->width; strcpy (T->ptr[1 ]->Snext, T->Snext); semantic_Analysis(T->ptr[1 ]); if (T->width < T->ptr[1 ]->width) T->width = T->ptr[1 ]->width; strcpy (T->ptr[2 ]->Snext, T->Snext); semantic_Analysis(T->ptr[2 ]); if (T->width < T->ptr[2 ]->width) T->width = T->ptr[2 ]->width; T->code = merge(6 , T->ptr[0 ]->code, genLabel(T->ptr[0 ]->Etrue), T->ptr[1 ]->code, genGoto(T->Snext), genLabel(T->ptr[0 ]->Efalse), T->ptr[2 ]->code); }
函数 genGoto
:生成 GOTO 语句,返回头指针
函数 genLabel
:生成标号语句
函数 boolExp
用于判断 Exp 是否为真
在 “Exp子句” 中会调用一次 genGoto
,根据条件跳转到 “if子句”,否则跳转到 “else子句”
在 “if子句” 中也会调用一次 genGoto
,跳转到 “if-else语句” 结束
当 “else子句” 执行完毕时,“if-else语句” 结束
注意:程序将直接执行 boolExp
,而不是去分析 T->ptr[0]
,这样会导致函数调用无法生效(函数调用的核心代码在 Exp 中)
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 void boolExp (struct node *T) { struct opn opn1 , opn2 , result ; int op; int rtn; if (T) { switch (T->kind) { case INT: if (T->type_int != 0 ) T->code = genGoto(T->Etrue); else T->code = genGoto(T->Efalse); T->width = 0 ; break ; case FLOAT: if (T->type_float != 0.0 ) T->code = genGoto(T->Etrue); else T->code = genGoto(T->Efalse); T->width = 0 ; break ; case ID: rtn = searchSymbolTable(T->type_id); if (rtn == -1 ) semantic_error(T->pos, T->type_id, "变量未定义,语义错误" ); if (symbolTable.symbols[rtn].flag == 'F' ){ semantic_error(T->pos, T->type_id, "是函数名,不是普通变量,语义错误" ); } else { opn1.kind = ID; strcpy (opn1.id, symbolTable.symbols[rtn].alias); opn1.offset = symbolTable.symbols[rtn].offset; opn2.kind = INT; opn2.const_int = 0 ; result.kind = ID; strcpy (result.id, T->Etrue); T->code = genIR(NEQ, opn1, opn2, result); T->code = merge(2 , T->code, genGoto(T->Efalse)); } T->width = 0 ; break ; case RELOP: T->ptr[0 ]->offset = T->ptr[1 ]->offset = T->offset; Exp(T->ptr[0 ]); T->width = T->ptr[0 ]->width; Exp(T->ptr[1 ]); if (T->width < T->ptr[1 ]->width) T->width = T->ptr[1 ]->width; opn1.kind = ID; strcpy (opn1.id, symbolTable.symbols[T->ptr[0 ]->place].alias); opn1.offset = symbolTable.symbols[T->ptr[0 ]->place].offset; opn2.kind = ID; strcpy (opn2.id, symbolTable.symbols[T->ptr[1 ]->place].alias); opn2.offset = symbolTable.symbols[T->ptr[1 ]->place].offset; result.kind = ID; strcpy (result.id, T->Etrue); if (strcmp (T->type_id, "<" ) == 0 ) op = JLT; else if (strcmp (T->type_id, "<=" ) == 0 ) op = JLE; else if (strcmp (T->type_id, ">" ) == 0 ) op = JGT; else if (strcmp (T->type_id, ">=" ) == 0 ) op = JGE; else if (strcmp (T->type_id, "==" ) == 0 ) op = EQ; else if (strcmp (T->type_id, "!=" ) == 0 ) op = NEQ; T->code = genIR(op, opn1, opn2, result); T->code = merge(4 , T->ptr[0 ]->code, T->ptr[1 ]->code, T->code, genGoto(T->Efalse)); break ; case AND: case OR: if (T->kind == AND) { strcpy (T->ptr[0 ]->Etrue, newLabel()); strcpy (T->ptr[0 ]->Efalse, T->Efalse); } else { strcpy (T->ptr[0 ]->Etrue, T->Etrue); strcpy (T->ptr[0 ]->Efalse, newLabel()); } strcpy (T->ptr[1 ]->Etrue, T->Etrue); strcpy (T->ptr[1 ]->Efalse, T->Efalse); T->ptr[0 ]->offset = T->ptr[1 ]->offset = T->offset; boolExp(T->ptr[0 ]); T->width = T->ptr[0 ]->width; boolExp(T->ptr[1 ]); if (T->width < T->ptr[1 ]->width) T->width = T->ptr[1 ]->width; if (T->kind == AND) T->code = merge(3 , T->ptr[0 ]->code, genLabel(T->ptr[0 ]->Etrue), T->ptr[1 ]->code); else T->code = merge(3 , T->ptr[0 ]->code, genLabel(T->ptr[0 ]->Efalse), T->ptr[1 ]->code); break ; case NOT: strcpy (T->ptr[0 ]->Etrue, T->Efalse); strcpy (T->ptr[0 ]->Efalse, T->Etrue); boolExp(T->ptr[0 ]); T->code = T->ptr[0 ]->code; break ; case FUNC_CALL: semantic_error(T->pos, T->type_id, "暂时不支持函数调用" ); break ; default : break ; } } }
WHILE:WHILE 语句(Exp,Stmt-语句节点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void while_dec (struct node *T) { strcpy (T->ptr[0 ]->Etrue, newLabel()); strcpy (T->ptr[0 ]->Efalse, T->Snext); T->ptr[0 ]->offset = T->ptr[1 ]->offset = T->offset; boolExp(T->ptr[0 ]); T->width = T->ptr[0 ]->width; strcpy (T->ptr[1 ]->Snext, newLabel()); semantic_Analysis(T->ptr[1 ]); if (T->width < T->ptr[1 ]->width) T->width = T->ptr[1 ]->width; T->code = merge(5 , genLabel(T->ptr[1 ]->Snext), T->ptr[0 ]->code, genLabel(T->ptr[0 ]->Etrue), T->ptr[1 ]->code, genGoto(T->ptr[1 ]->Snext)); }
程序会在 “Exp子句” 之前写入一个 LABEL labeln
在 “Exp子句” 中会调用一次 genGoto
,根据条件跳转到 “while子句”,否则结束 “while语句”
当 “while子句” 结束时会自动跳转到 “Exp子句” 开始新一轮循环
FOR:FOR 语句(FOR_DEC,Stmt-语句节点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void for_dec (struct node* T) { strcpy (T->ptr[0 ]->Etrue, newLabel()); strcpy (T->ptr[0 ]->Efalse, T->Snext); T->ptr[0 ]->offset = T->ptr[1 ]->offset = T->offset; semantic_Analysis(T->ptr[0 ]); strcpy (T->ptr[1 ]->Snext, newLabel()); semantic_Analysis(T->ptr[1 ]); if (T->width < T->ptr[1 ]->width) T->width = T->ptr[1 ]->width; T->code = merge(7 , T->ptr[0 ]->ptr[0 ]->code, genLabel(T->ptr[1 ]->Snext), T->ptr[0 ]->ptr[1 ]->code, genLabel(T->ptr[0 ]->ptr[1 ]->Etrue), T->ptr[1 ]->code, T->ptr[0 ]->ptr[2 ]->code, genGoto(T->ptr[1 ]->Snext)); }
函数 for_dec
调用后,函数 for_list
必被调用
ForDec:FOR 语句列表(Exp,Exp,Exp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void for_list (struct node* T) { if (T->ptr[0 ]!=NULL ){ T->ptr[0 ]->offset = T->ptr[2 ]->offset = T->offset; if (T->ptr[0 ]->kind == ASSIGNOP){ semantic_Analysis(T->ptr[0 ]); } else { semantic_error(T->pos, "" , "for wrong\n" ); } if (T->ptr[2 ]->kind != ASSIGNOP){ semantic_Analysis(T->ptr[2 ]); } else { semantic_error(T->pos, "" , "for wrong\n" ); } } T->ptr[1 ]->offset = T->offset; strcpy (T->ptr[1 ]->Etrue, T->Etrue); strcpy (T->ptr[1 ]->Efalse, T->Efalse); boolExp(T->ptr[1 ]); T->width = T->ptr[0 ]->width+T->ptr[1 ]->width+T->ptr[2 ]->width; }
不更新 T->code
,方便交给 for_dec
函数来控制顺序
基础运算
加减乘除:PLUS,MINUS,STAR,DIV(Exp,Exp)
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 op_exp (struct node *T) { struct opn opn1 , opn2 , result ; T->ptr[0 ]->offset = T->offset; Exp(T->ptr[0 ]); T->ptr[1 ]->offset = T->offset + T->ptr[0 ]->width; Exp(T->ptr[1 ]); T->type = INT, T->width = T->ptr[0 ]->width + T->ptr[1 ]->width + 2 ; T->place = temp_add(newTemp(), LEV, T->type, 'T' , T->offset + T->ptr[0 ]->width + T->ptr[1 ]->width); opn1.kind = ID; strcpy (opn1.id, symbolTable.symbols[T->ptr[0 ]->place].alias); opn1.type = T->ptr[0 ]->type; opn1.offset = symbolTable.symbols[T->ptr[0 ]->place].offset; opn2.kind = ID; strcpy (opn2.id, symbolTable.symbols[T->ptr[1 ]->place].alias); opn2.type = T->ptr[1 ]->type; opn2.offset = symbolTable.symbols[T->ptr[1 ]->place].offset; result.kind = ID; strcpy (result.id, symbolTable.symbols[T->place].alias); result.type = T->type; result.offset = symbolTable.symbols[T->place].offset; T->code = merge(3 , T->ptr[0 ]->code, T->ptr[1 ]->code, genIR(T->kind, opn1, opn2, result)); T->width = T->ptr[0 ]->width + T->ptr[1 ]->width + 4 ; }
函数 temp_add
生成的临时变量在赋值语句 ASSIGNOP 中才有作用
虽然 T->code
的次序为 opnstr1 opnstr2 op
,但在 print_IR
函数打印时可以调整它的次序
加加减减:PPLUS,MMINUS(Exp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void oop_exp (struct node *T) { struct opn opn1 , opn2 , result ; T->ptr[0 ]->offset = T->offset; Exp(T->ptr[0 ]); T->type = INT, T->width = T->ptr[0 ]->width; opn1.kind = ID; strcpy (opn1.id, symbolTable.symbols[T->ptr[0 ]->place].alias); opn1.type = T->ptr[0 ]->type; opn1.offset = symbolTable.symbols[T->ptr[0 ]->place].offset; T->code = merge(2 , T->ptr[0 ]->code, genIR(T->kind, opn1, opn2, result)); T->width = T->ptr[0 ]->width; }
最终的完整代码有 1200 多行,就不放出了
编译与结果
编译命令:
1 2 3 flex lexer.l bison -d -v parser.y gcc display.c semantic_analysis.c parser.tab.c lex.yy.c -lfl -o test3
测试文件1:
1 2 3 4 5 6 7 8 9 10 11 int fact (int n) { int temp; temp = n; if (n==1 ) n=2 ; else { n=3 ; } fact(n); return n; }
结果1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FUNCTION fact : PARAM var2 var3 := var2 temp1 := #1 IF var2 == temp1 GOTO label4 GOTO label5 LABEL label4 : temp2 := #2 var2 := temp2 GOTO label3 LABEL label5 : temp3 := #3 var2 := temp3 LABEL label3 : ARG var2 temp4 := CALL fact RETURN var2 LABEL label1 :
测试文件2:
1 2 3 4 5 6 7 8 9 10 11 int main (char c) { int c1 = 5 ; int c2 = 6 ; int c3 = 7 ; int c4 = 8 ; int c5 = 9 ; c3 = (c1+c2*c3)*(c5-c4); c1++; }
结果2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 FUNCTION main : PARAM var2 temp1 := #5 var3 := temp1 temp2 := #6 var4 := temp2 temp3 := #7 var5 := temp3 temp4 := #8 var6 := temp4 temp5 := #9 var7 := temp5 temp6 := var4 * var5 temp7 := var3 + temp6 temp8 := var7 - var6 temp9 := temp7 * temp8 var5 := temp9 var3 := var3 + 1 LABEL label1 :
测试文件3:
1 2 3 4 5 6 7 8 9 10 11 int n;int main (char c) { int c1 = 5 ; n = 0 ; while (c1>1 ){ n = n+1 ; } return n; }
结果3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FUNCTION main : PARAM var3 temp1 := #5 var4 := temp1 temp2 := #0 var1 := temp2 LABEL label5 : temp3 := #1 IF var4 > temp3 GOTO label4 GOTO label3 LABEL label4 : temp4 := #1 temp5 := var1 + temp4 var1 := temp5 GOTO label5 LABEL label3 : RETURN var1 LABEL label1 :
测试文件4:
1 2 3 4 5 6 7 8 9 10 int main (char c) { int n = 8 ; int i; for (i=10 ;i<n;i++){ n -= 2 ; } return i; }
结果4:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FUNCTION main : PARAM var2 temp1 := #8 var3 := temp1 temp2 := #10 var4 := temp2 LABEL label4 : IF var4 < var3 GOTO label3 GOTO label2 LABEL label3 : temp3 := #2 var3 := var3 - temp3 var4 := var4 + 1 GOTO label4 LABEL label2 : RETURN var4 LABEL label1 :