📄 main.c
字号:
TAC *mktac( int op, /* 操作码 */
SYMB *a, /* 第一操作数 */
SYMB *b, /* 第二操作数 */
SYMB *c ) /* 第三操作数 */
/* 构造一个三地址码的内存空间,其形参表示下述三地址码:
a := b op c
注意:VA,VB和VC的宏定义在vc.h中,分别表示三地址码的第一,第二和第三分
量取符号表元素的指针。由于每次调用mktac()时,只申请一个三地址码内存空间
因此,代码的效率有所降低。减少申请的次数,可以考虑每次申请一组三地址码
内存空间。*/
{
TAC *t = (TAC *)safe_malloc( sizeof( TAC )) ;
t->next = NULL ; /* 为了全起见,将向前和
向后指针指针设为空 */
t->prev = NULL ;
t->op = op ;
t->VA = a ;
t->VB = b ;
t->VC = c ;
return t ;
} /* TAC *mktac( int op,
SYMB *a,
SYMB *b,
SYMB *c ) 结束 */
TAC *join_tac( TAC *c1,
TAC *c2 )
/* 连接两段三地址码,将链c1尾和链c2首相连接,注意在语法分析时,我们总是取三地
址码链表的表尾表示该链表,因此,c1和c2都是表尾指针。连接c1和c2时,要通过c2
找到表头后再进行连接,由于join_tac()要返回连接后的三地址链表的表尾指针,即
c2,因此,在返回链c2的表头时,要保留c2,供返回之用。*/
{
TAC *t ;
/* 如果两个链表有一个为空,则返回另一个 */
if( c1 == NULL )
return c2 ;
if( c2 == NULL )
return c1 ;
t = c2 ; /* 保留c2供返回之用 */
/* 移动t到c2的表头,并连接两链表 */
while( t->prev != NULL )
t = t->prev ;
t->prev = c1 ;
return c2 ;
} /* TAC *join_tac( TAC *c1,
TAC *c2 ) 结束 */
/* 以下是处理符号表的函数,符号表是一个哈希表,其元素是指向符号表单元的指针,
由于临时变量,标号和函数都化入符号表一并处理,函数mkxxx()创建不同类型的符号
表单元。对符号表的操作有插入insert()和查看lookup()。这两个函数都使用哈希函
数hash()获得符号表数组的下标 */
void insert( SYMB *s )
/* 插入一个新的符号于符号表中,首先获得符号名(字符串)的哈希数,以该数作为
该符号在哈希表的槽,我们将相同哈希数的符号放入同一个槽中形成一个链表,
*/
{
int hv = hash( s->TEXT1 ) ;
s->next = symbtab[hv] ; /* 插入到链表的表头 */
symbtab[hv] = s ;
} /* void insert( SYMB *s ) 结束 */
SYMB *lookup( char *s )
/* 查找是否在符号表中是否有和s同名的符号,如果没有则返回空指针。 */
{
int hv = hash( s ) ;
SYMB *t = symbtab[hv] ;
while( t != NULL ) /* 在hv槽所对应的链表中查找符号名 */
if( strcmp( t->TEXT1, s ) == 0 )
break ;
else
t = t->next ;
return t ; /* 如果没找到,则t一定移动到链表的尾部,
即空返回NULL */
} /* SYMB lookup( char *s ) 结束*/
int hash( char *s )
/* 对给定的字符串返回哈希数。在此我们使用32位的整数(Borland C ver 3.5中由于整
数int只有16位,所以用长整数long)进行哈希计算,对字符串中的每个字符的取最低
位的4个字节(s[i] & 0xf)和已计算出的哈希数的最高位的4个字节(hv >> 28)进行按位
异或运算后,将运算结果放入到哈希数的最低4位,最后取哈希数整出哈希表的长度
HASHSIZE的余数作为返回值。注意:本段程序是和编译器相关的,它依赖正数的内部表
示,因此在移植代码时,一定要考虑位运算所对应的整数的机器表示。*/
{
long hv = 0 ;
int i ;
for( i = 0 ; s[i] != EOS ; i++ )
{
long v = (hv >> 28) ^ (s[i] & 0xf) ;
hv = (hv << 4) | v ;
}
hv = hv & 0x7fffffff ; /* 保证最后的哈希数是正数 */
return (int) (hv % HASHSIZE) ;
} /* int hash ( char *s ) */
/* 以下是打印出中间三地址码的函数。*/
void print_instr( TAC *i )
/* 函数print_instr()打印出三地址码,本函数通过switch语句对不同的操作码进行选择
打印出操作码对应的助忆符,并通过调用ts()函数,打印出相应的操作数。在代码生成
时,通过调用本函数将汇编对应的三地址作为注释输出 */
{
char sa[12] ; /* 操作数对应的文字 */
char sb[12] ;
char sc[12] ;
printf( " ", i ) ;
switch( i->op )
{
case TAC_UNDEF:
printf( "undef\n" ) ;
break ;
case TAC_ADD:
printf( "%s := %s + %s\n", ts( i->VA, sa ), ts( i->VB, sb ),
ts( i->VC, sc )) ;
break ;
case TAC_SUB:
printf( "%s := %s - %s\n", ts( i->VA, sa ), ts( i->VB, sb ),
ts( i->VC, sc )) ;
break ;
case TAC_MUL:
printf( "%s := %s * %s\n", ts( i->VA, sa ), ts( i->VB, sb ),
ts( i->VC, sc )) ;
break ;
case TAC_DIV:
printf( "%s := %s / %s\n", ts( i->VA, sa ), ts( i->VB, sb ),
ts( i->VC, sc )) ;
break ;
case TAC_NEG:
printf( "%s := - %s\n", ts( i->VA, sa ), ts( i->VB, sb )) ;
break ;
case TAC_COPY:
printf( "%s := %s\n", ts( i->VA, sa ), ts( i->VB, sb )) ;
break ;
case TAC_GOTO:
printf( "goto L%d\n", i->LA->VA->VAL1 ) ;
break ;
case TAC_IFZ:
printf( "ifz %s goto L%d\n", ts( i->VB, sb ),
i->LA->VA->VAL1 ) ;
break ;
case TAC_IFNZ:
printf( "ifnz %s goto L%d\n", ts( i->VB, sb ),
i->LA->VA->VAL1 ) ;
break ;
case TAC_ARG:
printf( "arg %s\n", ts( i->VA, sa )) ;
break ;
case TAC_CALL:
if( i->VA == NULL )
printf( "call L%d\n", i->LB->VA->VAL1 ) ;
else
printf( "%s = call L%d\n", ts( i->VA, sa ),
i->LB->VA->VAL1 ) ;
break ;
case TAC_RETURN:
printf( "return %s\n", ts( i->VA, sa )) ;
break ;
case TAC_LABEL:
printf( "label L%d\n", i->VA->VAL1 ) ;
break ;
case TAC_VAR:
printf( "var %s\n", ts( i->VA, sa )) ;
break ;
case TAC_BEGINFUNC:
printf( "beginfunc\n" ) ;
break ;
case TAC_ENDFUNC:
printf( "endfunc\n" ) ;
break ;
default:
/* opcode不是上述已知的代码 */
error( "未知三地址操作码" ) ;
printf( "未知三地址指令 %d\n", i->op ) ;
break ;
}
fflush( stdout ) ;
} /* print_instr( i ) 结束 */
char *ts( SYMB *s, /* 需要输出的符号 */
char *str ) /* 输出的符号名存入str之中*/
/* 由于符号有多种可能,因此通过switch语句,选择恰当的输出形式。 */
{
/* 检查符号指针是否为空 */
if( s == NULL )
return "NULL" ;
/* 确定符号的类型 */
switch( s->type )
{
case T_FUNC:
case T_VAR:
/* 对函数和变量返回其名 */
return s->TEXT1 ;
case T_TEXT:
/* 对字符串常量返回其地址 */
sprintf( str, "L%d", s->VAL2 ) ;
return str ;
case T_INT:
/* 对正数常量将其值转换为对应的字符串返回 */
sprintf( str, "%d", s->VAL1 ) ;
return str ;
default:
/* 如果符号的类型未知 */
error( "操作数类型未知" ) ;
return "?" ;
}
} /* ts( SYMB *s,
char *str ) 结束 */
void error( char *str )
/* 保错函数,本函数将在编译过程中产生的错误信息str及所在的行号输出到标准出错输
出,并且设置错误标记error_found为真,在此标记下,将执行代码生成程序。*/
{
fprintf( stderr, "vc: %s at line %d\n", str, lineno ) ;
errors_found = TRUE ;
} /* void error( char *str ) 结束 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -