1. 程式人生 > 實用技巧 >編譯原理-針對任意文法的遞迴下降分析

編譯原理-針對任意文法的遞迴下降分析

要求

遞迴下降分析

  1. 對文法進行LL(1)判別,若不是LL(1)文法,則進行等價變換。構造預測分析表。
  2. 程式設計實現遞迴下降分析。

完成情況

在完成已有實驗要求的基礎上,增加實現以下功能:

  1. 使之可以針對任意文法實現遞迴下降分析。(對應的文法規則和預測分析表從檔案中讀入)
  2. 使用括號表示法輸出語法分析樹。

程式碼實現

#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;
}

測試結果

正確串


錯誤串

可自行選擇測試用例。