/*Powered by Keamou@CS@CITS@NKU */
1. 将所有的词法分析功能均放在yygettoken函数内实现,为+、-、*、/、(、)每个运算符及整数分别定义一个单词类别,在yygettoken内实现代码,能识别这些单词,并将单词类别返回给词法分析程序。
答:
定义单词类别定义如下:
记号 | 类别名 |
+ | ADD |
- | SUB |
* | MUL |
/ | DIV |
( | LE |
) | RE |
数字 | NUMBER |
字母 | LETTER |
文件尾 | FEND |
Yacc程序获得输入的字符是通过int yygettoken(void) 函数获得的,因此可以在int yygettoken(void)函数里先分析输入字符,根据上表返回相应的类别名。
在符号定义段里:
%token NUMBER
%token ADD
%token SUB
%token MUL
%token DIV
%token LETTER
%token LE //左括号
%token RE //右括号
%token FEND //文件尾标志
%left ADD SUB
%left MUL DIV
%right UMINUS
因此,例如,中缀变后缀的语法定义段程序为:
lines : lines expr FEND { fprintf(fc, "Here is the postfix expr : %s/n", $2); printf("/nHere is the postfix expr : %s/n", $2); exit(1); }
| lines FEND { exit(1); }
|
;
expr : expr ADD expr { strcpy($$, $1); strcat($$, $3); strcat($$,"+"); }
| expr SUB expr { strcpy($$, $1); strcat($$, $3); strcat($$,"-"); }
| expr MUL expr { strcpy($$, $1); strcat($$, $3); strcat($$,"*"); }
| expr DIV expr { strcpy($$, $1); strcat($$, $3); strcat($$,"/"); }
| LE expr RE { strcpy($$, $2); }
| SUB expr %prec UMINUS { strcpy($$, $2); strcat($$,"-"); }
| alpha { strrev( $$ ); } //符号逆转
| digit { strrev( $$ ); } //符号逆转
;
alpha: LETTER { sprintf($$,"%c",pchar); }
| alpha LETTER { sprintf($$,"%s%c",$1,pchar); }
;
digit : NUMBER { itoa( num - '0' ,$$ ,10 ); }
| digit NUMBER { itoa( num - '0' ,$$ ,10 ); strcat($$ , $1); }
;
在int yygettoken(void) 里识别词素并返回词素类别,代码如下:
int yygettoken(void)
{
// place your token retrieving code here
int token;
token = fgetc(fp);
while ( token == ' ' || token == '/n' || token == '/t' ) //如果是空格、回车、制表符,则再读取文件的下一个字符
{
token = fgetc(fp);
}
if(feof(fp)) //如果到了文件尾,则返回符号FEND
{
return FEND ;
}
printf("%c",token); //在屏幕上输出从文件读出的非空白符的字符
switch(token)
{
case '+':
return ADD;
break;
case '-':
return SUB;
break;
case '*':
return MUL;
break;
case '/':
return DIV;
break;
case '(':
return LE;
break;
case ')':
return RE;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
num = token ;
return NUMBER;
break;
default:
if( ( token >= 'a' && token <= 'z' ) || ( token >= 'A' && token <= 'Z' ) )
{
pchar = token ;
return LETTER;
}
else
return token;
}
}
2. 实现功能更强的词法分析程序,可识别并忽略空格、制表符、回车等空白符,能识别多位十进制整数。
答:
修改int yygettoken(void)函数以支持忽略空格、制表符、回车:
int yygettoken(void)
{
int token;
token = getchar();
while(token == ' ' || token == '/n' || token == '/t' )
token = getchar();
return token;
}
在语法规则段里添加如下代码可实现多位十进制整数的算术运算:
DIGIT : NUMBER { $$ = $1; }
| NUMBER DIGIT { $$ = $1 * 10 + $2;}
;
若添加如下代码则可实现多位十进制整数的字符串表示:
digit : NUMBER { strcpy($$, $1); }
| NUMBER digit { strcpy($$, $1); strcat($$, $2);}
|
;
3. 修改Yacc程序,不进行表达式的计算,而是实现中缀表达式到后缀表达式的转换。
答:
由于老师已经把实现中缀表达式到后缀表达式的转换的Yacc程序上传了,为了使本题更有趣,我将上面两所实现功能都在本题里体现。
除此之外,为了体验在Yacc里的文件操作以及输入的方便,我还实现了文件读写的功能。用fp指针打开输入文件,读取expr.txt文件(此文件储存要转换的中缀表达式),用fc打开输出文件,结果把转换后的后缀表达式储存在postfix.txt文件中。只所以不采用main(argc,argv[])的方法是因为这样比较方便,不用多次输入文件名。
同时,也将输入和输出的内容在屏幕上显示,以随时便捷地验证结果的正确。
(思考)实现符号表功能。
答:
通过定义结构将遇到的符号和其值添加到符号表中:
struct tokenlist //符号表结构
{
char token[30];
float value;
struct tokenlist * next ;
}*head,*temp;
当遇到一个符号时,先判断是否已经在符号表里,在将其值添加或修改。如果是数字,则直接存储其值,如果是字母,若尚无存储,则初值为0。代码如下:
void storetoken(char* tn, float tv )
{
temp = head ;
while (temp != NULL ) //首先判断一下要存储的符号是否已经在符号表里
{
if( strcmp ( temp->token , tn ) == 0 )
{
temp->value = tv ;
return;
}
temp = temp->next ;
}
temp = head ;
head = (struct tokenlist*)malloc(sizeof (struct tokenlist));
strcpy(head->token , tn) ;
head->value = tv ;
head->next = temp;
}
为了从符号表里取出符号的值,编写了如下代码:
float findtoken(char* tn)
{
temp = head ;
while (temp != NULL )
{
if( strcmp ( temp->token , tn ) == 0 )
return temp->value ;
temp = temp->next ;
}
return 0 ;
}
为了能显示符号表的内容,编写了如下代码:
void displaylist()
{
temp = head ;
printf("---------The Token List-----------/n");
while ( temp != NULL )
{
printf("%s/t%lf/n",temp->token,temp->value);
temp = temp->next ;
}
printf("--------------End-----------------/n");
}
在编译的过程中,实现无用的符号从符号表里删除:
void deletetoken(char* tn)
{
temp = head ;
if( temp != NULL )
if ( strcmp ( temp->token , tn ) == 0 )
{
head = temp->next ;
free( temp ) ;
return;
}
while ( temp->next != NULL )
{
if ( strcmp ( temp->next->token , tn ) == 0 )
{
struct tokenlist * othertmp = temp->next ;
temp->next = temp->next->next ;
free( othertmp ) ;
return;
}
temp = temp->next ;
}
return;
}