編譯原理-針對任意文法的遞迴下降分析
阿新 • • 發佈:2021-01-11
要求
遞迴下降分析
- 對文法進行LL(1)判別,若不是LL(1)文法,則進行等價變換。構造預測分析表。
- 程式設計實現遞迴下降分析。
完成情況
在完成已有實驗要求的基礎上,增加實現以下功能:
- 使之可以針對任意文法實現遞迴下降分析。(對應的文法規則和預測分析表從檔案中讀入)
- 使用括號表示法輸出語法分析樹。
程式碼實現
#include<stdio.h> //以下均為最大長度,可根據需求更改 #define vt_n 100 #define vn_n 100 #define gra_n 20 #define gra_len 20 #define ERROR -1 //sym為當前可匹配字元 //a[]暫存輸入串,且最大長度為50 char sym,a[50]; int ip=0; //當前讀入文法規則及其數目 int vt_num; int vn_num; int gra_num; char vt[vt_n]; char vn[vn_n]; char grammar[gra_n][gra_len]; int pre[vn_n][vt_n]; struct Tnode{ char c; int num; //子節點的個數 struct node * next[10]; }; void init(); void cin(); //pvn位當前所處的狀態(即非終結符),p為傳入的指向當前節點指標的指標 int RD( char pvn, struct Tnode **p); //T為指向樹根節點的頭指標 void printfT( struct Tnode *T ); void DTree( struct Tnode *T ); void getNextSym(); struct Tnode * JTree( char c ); //================================================ void main() /*遞迴分析*/ { int ret; struct Tnode *T; //指向根節點的指標 init(); cin(); while(1){ T=NULL; printf("\ninput length < 50,ending with'#'; '^#' to return!\n"); ip=0; do{ scanf("%c",&sym); a[ip++]=sym; }while(sym!='#'); if(a[0]=='^' && a[1]=='#') return; printf("......begin......\n"); ip=-1; getNextSym(); ret=RD( 'S', &T); if (ret!= ERROR && sym=='#') printf("......accept!\n"); else printf("sym=%c......error!\n",sym); //可以選擇正確時再輸出分析樹,此處為了顯示錯誤結果 printfT(T); DTree(T); getchar(); } } //================================================ void init(){ /*初始化*/ memset( vt, '/0', sizeof(vt)); memset( vn, '/0', sizeof(vn)); memset( grammar, '\0', sizeof(grammar)); memset( pre, -1, sizeof(pre)); return ; } //================================================ void cin(){ /*文法讀入函式*/ FILE * fp; int i,j; //此處修改要讀取的文法檔案 fp=fopen("./test2.txt", "r"); fscanf( fp, "%s", vt); fscanf( fp, "%s", vn); for( i=0; i<vt_n; i++) if( vt[i]=='\0' ){ vt_num=i; break; } for( i=0; i<vn_n; i++) if( vn[i]=='\0' ){ vn_num=i; break; } fscanf( fp, "%d", &gra_num); for( i=0; i<gra_num; i++) fscanf( fp, "%s", grammar[i]); for( i=0; i<vn_num; i++) for( j=0; j<vt_num; j++) fscanf( fp, "%d", &pre[i][j]); fclose(fp); return ; } //================================================ void getNextSym(){ /*取輸入串的下一字元*/ sym=a[++ip]; printf(" sym=%c\n",sym); } //================================================ struct Tnode * JTree( char c ){ /*建立新結點*/ struct Tnode * pnode; pnode=( struct Tnode *)malloc(sizeof( struct Tnode ) ); pnode->c=c; pnode->num=0; return pnode; }; //================================================ void printfT( struct Tnode *T ){ /*遍歷輸出樹*/ int i=0; if( T->num==0 ){ printf("%c",T->c); return ; } printf("%c ",T->c); printf("("); for( i=0; i<T->num; i++) printfT( T->next[i] ); printf(")"); return ; } //================================================ void DTree( struct Tnode *T ){/*銷燬樹*/ int i; if( T->num==0 ){ free( T ); return ; } for( i=0; i<T->num; i++) DTree( T->next[i] ); free(T); } //================================================ int RD( char pvn, struct Tnode **p){ /*遞迴下降分析*/ int i; //迴圈變數 int row=-1; //預測分析錶行下標 int tmp=-1; //預測分析表列下標,即對應語句下標 struct Tnode *pnode, *tmp_node; //查詢此時非終結符對應的下標 for( i=0; i<vn_num; i++) if( pvn==vn[i] ) row=i; //查表找到對應語句下標 for( i=0; i<vt_num; i++) if( sym==vt[i] ) tmp=pre[row][i]; //可以分開判斷是錯誤字元還是沒匹配上 if( tmp==-1 || tmp==0 ) return ERROR; //建當前節點 pnode=JTree(pvn); *p=pnode; //找到對應的語句了,進行拆分 for( i=2; grammar[tmp][i]!='#'; i++){ //printf("grammar=%c\n",grammar[tmp][i]); if( grammar[tmp][i]<'A' || grammar[tmp][i]>'Z' ){ if( grammar[tmp][i]=='^' ){//樹顯示^表示替換為空串 //遇到終結符直接建立子節點並將其加入。下同 pnode->next[pnode->num++]=JTree(grammar[tmp][i]); return 0; } if( sym==grammar[tmp][i] ){ pnode->next[pnode->num++]=JTree(grammar[tmp][i]); printf("%s\n",grammar[tmp]); getNextSym(); } else//此處可新增對語法分析樹的構造以保證語法分析樹的完整 return ERROR; } else{//並非直接return RD(),否則判斷成功後將導致後續的非終結符無法判斷 if( RD( grammar[tmp][i], &pnode->next[pnode->num++])==ERROR ) return -1; } } return 0; }
測試結果
正確串
錯誤串
可自行選擇測試用例。