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

📄 parser.y

📁 《编译方法》课程设计内容2.《编译方法》课程设计内容
💻 Y
📖 第 1 页 / 共 2 页
字号:
/******************************************************************************
*******************************************************************************

				   parser.y
		
				语法分析yacc源文件 
	     
   本文件定义了VSL语言的语法,及对应的语义处理和中间代码生成函数。
   因此由yacc生成的函数yyparse()能完成VSL语言的中间代码生成。

   文件修改时间
   ============

   2005年5月7日:  Yacc源文件   (C) hfwang
 
*******************************************************************************
******************************************************************************/

%{

/* 定义部分 */

/* 本模块所需的头文件,注意:没有parser.h,因为它是由yacc分析本文件的一个输出文
   件。*/

#include <stdio.h>
#include <ctype.h>
#include "vc.h"

/* 以下是在本文件用户代码部分所定义的函数的原形。这些程序将用于词法分析中。*/

TAC   *do_program( TAC *c ) ;

TAC   *do_func( SYMB *func,
	      TAC  *args,
	      TAC  *code ) ;

TAC   *declare_var( SYMB *var ) ;

TAC   *do_assign( SYMB  *var,
		ENODE *expr ) ;

ENODE *do_bin(  int    binop,
		ENODE *expr1,
		ENODE *expr2 ) ;

ENODE *do_un(  int    unop,
	       ENODE *expr ) ;

ENODE *do_fnap( SYMB  *func,
		ENODE *arglist ) ;

TAC   *do_lib( int   rtn,
	     SYMB *arg ) ;

TAC   *do_if( ENODE *expr,
	    TAC   *stmt ) ;

TAC   *do_test( ENODE *expr,
	      TAC   *stmt1,
	      TAC   *stmt2 ) ;

TAC   *do_while( ENODE *expr,
	       TAC   *stmt ) ;

ENODE *mkenode( ENODE *next,
		SYMB  *res,
		TAC   *code ) ;

void  yyerror( char *str ) ;

%}

/* %union定义了语义值的属性,有符号指针,三地址码指针和表达式指针三种属性,符
   号指针对应于词法分析产生的终结符:标识符、整数常量和字符串常量;三地址指针
   对应于一般的非终结符;而表达式指针用于处理对表达式的分析与翻译,表达式结构
   记录了表达式翻译对应的三地址码和保存中间结果的临时变量。 */

%union
{
	SYMB   *symb ;		 /* 变量,常数和字符串的语义值 */
	TAC    *tac ;		 /* 语句的语义值 */
	ENODE  *enode ;		 /* 表达式的语义值 */
}

%{

/* yacc将%union翻译为一个类型名为YYSTYPE的共用体的类型定义。在此定义全局变量
   finaltac记录最后产生的三地址码指针,利用该指针,我们在目标代码生成时可以
   找到三地址码链的头节点。*/

YYSTYPE finaltac;

%}


/* 定义词汇(终结符)及对应的语义属性。只有标识符VARIABLE、整数常量INTEGER和字
   符串常量TEXT有符号指针属性。*/

%token         FUNC			 /* 关键字'FUNC' */
%token <symb>  VARIABLE			 /* 标识符 */
%token         ASSIGN_SYMBOL		 /* ':=' */
%token <symb>  INTEGER			 /* 整数常量 */
%token         PRINT			 /* 关键字'PRINT' */
%token <symb>  TEXT			 /* 由双引号所引的字符串常量 */
%token         RETURN			 /* 关键字'RETURN' */
%token         CONTINUE			 /* 关键字'CONTINUE */
%token         IF			 /* 关键字'IF' */
%token         THEN			 /* 关键字'THEN' */
%token         ELSE			 /* 关键字'ELSE' */
%token         FI			 /* 关键字'FI' */
%token         WHILE			 /* 关键字'WHILE' */
%token         DO			 /* 关键字'DO' */
%token         DONE			 /* 关键字'DONE' */
%token         VAR			 /* 关键字'VAR' */
%token         UMINUS			 /* 一元减,虚拟词汇 */

/* 定义非终结符及对应的属性,除表达式expression、表达式列表expression_list和
   参数列表argument_list的语义属性为表达式指针外,其余的非终结符的语义属性为
   三地址指针。*/

%type <tac>     program
%type <tac>     function_list
%type <tac>     function
%type <tac>     parameter_list
%type <tac>     variable_list
%type <enode>   argument_list
%type <enode>   expression_list
%type <tac>     statement
%type <tac>     assignment_statement
%type <enode>   expression
%type <tac>     print_statement
%type <tac>     print_list
%type <tac>     print_item
%type <tac>     return_statement
%type <tac>     null_statement
%type <tac>     if_statement
%type <tac>     while_statement
%type <tac>     block
%type <tac>     declaration_list
%type <tac>     declaration
%type <tac>     statement_list
%type <tac>     error

/* 定义算术运算的优先级别和结合次序:伪终结符UMINUS是为了定义一元减的
   优先级。*/

%left  '+' '-'
%left  '*' '/'
%right UMINUS

%%


/* 语法规则部分 */

/* 以下定义了VSL语言的语法规则及对应的语义操作,在语法分析之后,产生程序的三地
   址码。注意缺省的语义操作是:$$ = $1 。*/


/* program是文法开始符号,这是自底向上分析的最后一部,此时function_list的语义
   值是指向整个程序的三地址码链的链尾的指针,我们用全局变量finaldoc记录该指针,
   在代码生成时用该指针找到三地址链表的表头。*/

   program                 :       function_list
                                {finaltac.tac = $1;}
			;

/* function_list有两个产生式,对第二个产生式,我们用join_tac()将产生式右边的两
   个三地址链表链结在一起,将返回的表尾指针作为产生式左边符号的语义值,join_tac()
   函数在链结两表时必须将后一个三地址链的表尾指针移动到表头,这样在程序过大时会
   降低编译器的执行效率,在实际的编译器中可以采用不链结函数的三地址码链,而是将
   函数的三地址码的尾指针保存在一个数据结构中,以提高编译器的效率。*/

function_list		:	function
			|	function_list function
				{
					$$ = join_tac( $1, $2 ) ;
				}
			;

/* 函数定义语句的形式文法和翻译,首先将临时变量计数其清零,因为临时变量仅在函数
   调用时才动态分配存储空间,所以,不同函数的临时变量可以同名;又因为临时变量仅
   是为了记录表达式运算的中间结果,没有初值,因此不同函数的临时变量可以共享同一
   个符号单元,对函数定义语句的翻译由函数do_func()完成。

   在语法分析出错时,通过yacc的特殊终结符error对语法分析进行恢复,同时调用error()
   函数报错,这是最简单的出错处理,在实际的编译器中,一般用定义同步符号集等方法
   对错误进行恢复。*/

function		:	FUNC VARIABLE '(' parameter_list ')'
			        statement
				{
					next_tmp = 0 ;
					$$ = do_func( $2, $4, $6 ) ;
				}
			|	error
				{
					error( "Bad function syntax" ) ;
					$$ = NULL ;
				}
			;

/* 参数列表:或者是变量名(形参)列表,或者是空,表示无参函数。*/

parameter_list		:	variable_list
			|
				{
					$$ = NULL ;
				}
			;

/* 变量名列表,在此我们将形参当变量等同处理,由于,变量的内存地址在代码生成阶段确
   定通过,因此处理变量时,通过引入三地址码指令TAC_VAR记录变量定义语句,在代码生成
   时通过该三地址码指令分配变量的内存在,declare_var()产生变量定义语句,*/

variable_list		:	VARIABLE
				{
					$$ = declare_var( $1 ) ;
				
					/* $1是词法分析函数yylex()识别变量后
					   定义的yylval */
				}		
			|	variable_list ',' VARIABLE
				{
					TAC *t = declare_var( $3 ) ;

					t->prev = $1 ;
					/* 链结三地址码 */
					$$ = t ;
				}		
			;


statement		:	assignment_statement
			|	return_statement
			|	print_statement
			|	null_statement
			|	if_statement
			|	while_statement
			|	block
			|   	error
				{
					error( "Bad statement syntax" ) ;
					$$ = NULL ;
				}
			;

assignment_statement	:	VARIABLE ASSIGN_SYMBOL expression
				{
					$$ = do_assign( $1, $3 ) ;
				}
			;

/* 表达式语义翻译, 注意通过引入虚拟词汇%prec UMINUS处理一元减 */

expression		:	expression '+' expression
				{
					$$ = do_bin( TAC_ADD, $1, $3 ) ;
				}
			|	expression '-' expression
				{
					$$ = do_bin( TAC_SUB, $1, $3 ) ;
				}
			|	expression '*' expression
				{
					$$ = do_bin( TAC_MUL, $1, $3 ) ;
				}
			|	expression '/' expression
				{
					$$ = do_bin( TAC_DIV, $1, $3 ) ;
				}
			|	'-' expression  %prec UMINUS
				{
					$$ = do_un( TAC_NEG, $2 ) ;
				}
			|	'(' expression ')'
				{
					$$ = $2 ;
				}		
			|	INTEGER
				{
					$$ = mkenode( NULL, $1, NULL ) ;
				}
			|	VARIABLE
				{
					$$ = mkenode( NULL, $1, NULL ) ;
				}
			|	VARIABLE '(' argument_list ')'
				{
					$$ = do_fnap( $1, $3 ) ;
				}		
			|   	error
				{
					error( "Bad expression syntax" ) ;
					$$ = mkenode( NULL, NULL, NULL ) ;
				}
			;

argument_list		:
				{
					$$ = NULL ;
				}
			|	expression_list
			;

expression_list		:	expression
			|	expression_list ',' expression
				{

					/* 产生表达式链表 */

					$1->next = $3 ;
					$$       = $1 ;
				}
			;

print_statement		:	PRINT print_list
				{
					$$ = $2 ;
				}		
			;

print_list		:	print_item
			|	print_list ',' print_item
				{
					$$ = join_tac( $1, $3 ) ;
				}		
			;

/* 打印项目将翻译为调用库函数,通过函数do_lib()产生调用打印库函数的三地址码,
   通过join_tac()将该三地址码链接到三地址码链上 */

print_item		:	expression
				{

					/* 调用打印库函数,其中$1->res是
					   表达式临时变量在符号表的指针 */

					$$ = join_tac( $1->tac,
						       do_lib( LIB_PRINTN,
						       $1->res )) ;
				}
			|	TEXT
				{

					/* 同上,$1是字符串常量在符号表的指针 */

					$$ = do_lib( LIB_PRINTS, $1 ) ;
				}
			;

return_statement	:	RETURN expression
				{
					TAC *t = mktac( TAC_RETURN, $2->res,
							NULL, NULL ) ;
					t->prev = $2->tac ;
					free_enode( $2 ) ;
					$$      = t ;
				}		
			;

null_statement		:	CONTINUE
				{
					$$ = NULL ;
				}		
			;

/* 在此对匹配的if和不匹配的if采用不同的翻译方法. */

if_statement		:	IF expression THEN statement FI
				{
					$$ = do_if( $2, $4 ) ;
				}
			|	IF expression THEN statement
			        ELSE statement FI
				{
					$$ = do_test( $2, $4, $6 ) ;
				}
			;

while_statement		:	WHILE expression DO statement DONE
				{
					$$ = do_while( $2, $4 ) ;
				}		
			;

block			:	'{' declaration_list statement_list '}'
				{
					$$ = join_tac( $2, $3 ) ;
				}		
			;

declaration_list	:
				{
					$$ = NULL ;
				}
			|	declaration_list declaration
				{
					$$ = join_tac( $1, $2 ) ;
				}
			;

declaration		:	VAR variable_list
				{
					$$ = $2 ;
				}
			;

statement_list		:	statement

			|	statement_list statement
				{
					$$ = join_tac( $1, $2 ) ;
				}		
			;

%%

/* 一些是处理各类语句翻译的函数,这样处理将简化语法规则中的语义动作,并使得
   有些翻译动作可以在动作部分重复使用 */


TAC *do_func( SYMB *func,	 		 /* 函数名在 */
	      TAC  *args,	 		 /* 参数列表 */
	      TAC  *code )	 		 /* 函数体的四元式链 */

/* 函数定义语句的翻译: 首先对函数体四元式代码的首尾加上TAC_BEGINFUNC和
   TAC_ENDFUNC标签, 然后查看函数名的类型, 该函数名在符号表中的类型必须
   是未定义T_UNDEF类型, 否则表示该函数名已经被定义过, 

   由于VSL语言的函数可以是先使用后定义,在这种情况下,函数调用时还不
   知道函数的入口地址, 我们将调用语句的四元式地址链接到函数名的符号表
   单元的LABEL2上, 因此函数定义语句如果检测到LABEL2非空, 将回填函数的入口

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -