⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 《编译方法》课程设计内容2.《编译方法》课程设计内容
💻 C
📖 第 1 页 / 共 2 页
字号:
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 + -