/*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;
}