c语言程序实现递归向下分析及语法分析树并打印

  • Post author:
  • Post category:其他


文法如下:

文法

如果要进行递归下降分析,肯定少不了FIRST集,FOLLOW集,SELECT集和预测分析表。并根据预测分析表或者SELECT集判断该文法是不是LL(1)文法。


怎么求FIRST集?

请添加图片描述

下面的代码是第一版,不能输出语法分析树。

#include <stdio.h>
#define ERROR -1                //程序不能正确运行的返回值
char sym,a[50];                 //sym临时存放一个字符,数组a存放输入的字符串
int ip=0;                       //字符数组下标
int A();                        //函数声明
int B();
int C();
int X();
int Y();

void getNextSym()               //取字符串中的下一个字符
{
    sym=a[++ip];
    printf("  sym=%c\n",sym);   //输出当前字符
}

//================================================
void main()                     //主函数
{
	int ret;                    //存放其他函数的返回值
	while(1){
        //下面这句输出语句对用户输入的字符串作出约束
        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=A();                      //只有函数A()返回值为0,才说明分析过程正确
        if (ret != ERROR && sym=='#') //当所有函数正确执行并且分析到最后一个字符
            printf("*****ACCEPT!!!*****\n");
        else
            printf("-----!ERROR!------\n");
        getchar();
	}
}


//================================================
int A()
{
    if(sym == '['){                 //只有当输入的第一个字符为[,才能继续向下分析
        printf("A->[B\n");          //输出文法对应的产生式
        getNextSym();               //取下一个字符
        if(B()==ERROR)              //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;           //结束分析过程
    }
    else                            //如果第一个字符就不是[,结束分析过程
        return ERROR;
    return 0;                       //分析成功,正常结束
}

int B()
{
    if(sym == 'a'||sym == 'b')      //当字符是a或b时,才继续向下分析
    {
        printf("B->X]C\n");         //输出文法对应的产生式
        if(X()==ERROR)              //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;           //结束分析过程
        if(sym != ']')              //分析文法,此时的字符只有是]才能够继续分析
            return ERROR;
        getNextSym();
        if(C()==ERROR)              //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;           //结束分析过程
    }
    else                            //如果当前字符不是a或b,结束分析过程
        return ERROR;
    return 0;                       //分析成功,正常结束
}

int C()                             //以下的程序都可以参考函数A和函数B的注释
{
    if(sym == 'a')
    {
        printf("C->aC\n");
        getNextSym();
        if(C()==ERROR)
            return ERROR;
    }
    else if(sym == '#'){            //如果当前字符是#,说明字符串已经全部分析完了
        printf("C-> \n");
    }
    else
        return ERROR;
    return 0;
}

int X()
{
    if(sym == 'a')
    {
        printf("X->aY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == 'b')
    {
        printf("X->bY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else
        return ERROR;
    return 0;

}

int Y()
{
    if(sym == 'a')
    {
        printf("Y->aY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == 'b')
    {
        printf("Y->bY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == ']'){//这个地方比较关键,如果这个条件成立,因为此时已经把[取出来了
                        //所以返回到函数B就不用取这个字符了,直接到下一个字符
        printf("Y-> \n");
    }
    else
        return ERROR;
    return 0;
}

下面的代码是第二版,输出语法分析树。

#include <stdio.h>
#include <string.h>

#define ERROR -1                //程序不能正确运行的返回值
char sym,a[50];//sym临时存放一个字符,数组a存放输入的字符串
int ip=0;                       //字符数组下标
int A();                        //函数声明
int B();
int C();
int X();
int Y();
int arr[100][100];//这个数组用于辅助打印语法分析树
int row;//arr数组的行
int col;//arr数组的列
int first_C;//如果是第一次到C函数,值是1,之后为0;

void getNextSym()               //取字符串中的下一个字符
{
    sym=a[++ip];
    printf("  sym=%c\n",sym);   //输出当前字符
}
//================================================
int main()                      //主函数
{
	int ret;                    //存放其他函数的返回值
	while(1){
        //下面这句输出语句对用户输入的字符串作出约束
        int i=0;
        int j=0;
        memset(arr,' ',sizeof(arr));
        //每次进入while循环就初始化数组全为空字符
        row=0;
        col=0;
        first_C=1;
        //因为下面的二维数组中的元素无论在什么串里,只要串是正确的
        //在打印的时候都是一样的,而错误的串又没必要输出语法分析树
        //所以可以先存放到数组里
        arr[0][3]='A';
        arr[1][2]='/';          //表示父节点和子节点的从属关系
        arr[1][4]='\\';         //注意'\'是转义字符
        arr[2][1]='[';
        arr[2][4]='B';
        arr[3][3]='/';
        arr[3][4]='|';
        arr[3][5]='\\';
        arr[4][2]='X';
        arr[4][4]=']';
        arr[4][6]='C';
        arr[5][1]='/';
        arr[5][3]='\\';
        //arr[5][7]='\\';
        row=6;//在定义了上面数组元素后,数组的起始位置
        col=0;

        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 0;

        printf("......begin......\n");//分析开始
        ip=-1;                        //重新设置字符数组下标
        getNextSym();
        ret=A();                      //只有函数A()返回值为0,才说明分析过程正确
        if (ret != ERROR && sym=='#') //当所有函数正确执行并且分析到最后一个字符
        {
            printf("*****ACCEPT!!!*****\n");
            for(i=0;i<30;i++)              //打印语法分析树
            {
                for(j=0;j<50;j++)
                {
                    printf("%c",arr[i][j]);
                }
                printf("\n");
            }
        }
        else
            printf("-----!ERROR!------\n");
        getchar();
	}
	return 0;
}
//================================================
int A()
{
    if(sym == '['){             //只有当输入的第一个字符为[,才能继续向下分析
        printf("A->[B\n");      //输出文法对应的产生式
        getNextSym();           //取下一个字符
        if(B()==ERROR)          //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;       //结束分析过程
    }
    else                        //如果第一个字符就不是[,结束分析过程
        return ERROR;
    return 0;                   //分析成功,正常结束
}

int B()
{
    if(sym == 'a'||sym == 'b')  //当字符是a或b时,才继续向下分析
    {
        printf("B->X]C\n");     //输出文法对应的产生式
        if(X()==ERROR)          //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;       //结束分析过程
        if(sym != ']')          //分析文法,此时的字符只有是]才能够继续分析
            return ERROR;
        getNextSym();
        if(C()==ERROR)          //如果接下来的字符在文法中没有匹配的产生式
            return ERROR;       //结束分析过程
    }
    else                        //如果当前字符不是a或b,结束分析过程
        return ERROR;
    return 0;                   //分析成功,正常结束
}

int C()                         //以下的程序都可以参考函数A和函数B的注释
{
    if(first_C==1)              //如果是第一次进入C函数,需要重新定位
    {
        row=5;
        col=7;
    }
    if(sym == 'a')
    {
        first_C=0;              //之后再进入C函数就不是第一次了,所以修改标记
        arr[row][col]='\\';     //接下来的四句代码就是画树和重新定位
        arr[row+1][col+1]=sym;
        row+=2;
        col+=2;
        printf("C->aC\n");
        getNextSym();
        if(C()==ERROR)
            return ERROR;
    }
    else if(sym == '#'){        //如果当前字符是#,说明字符串已经全部分析完了
        arr[row][col]='\\';
        arr[row+1][col+1]='ε';  //空
        printf("C-> \n");
    }
    else
        return ERROR;
    return 0;
}

int X()
{
    if(sym == 'a')
    {
        arr[row][col]=sym;
        arr[row][col+3]='Y';
        row+=1;
        col+=3;
        printf("X->aY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == 'b')
{
arr[row][col]=sym;
arr[row][col+3]='Y';
row+=1;
col+=3;
        printf("X->bY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else
        return ERROR;
    return 0;

}

int Y()
{
    if(sym == 'a')
    {
        arr[row][col-1]='/';
        arr[row][col+1]='\\';
        row+=1;
        col-=2;
        arr[row][col]=sym;
        arr[row][col+3]='Y';
        row+=1;
        col+=3;
        printf("Y->aY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == 'b')
    {
        arr[row][col-1]='/';
        arr[row][col+1]='\\';
        row+=1;
        col-=2;
        arr[row][col]=sym;
        arr[row][col+3]='Y';
        row+=1;
        col+=3;
        printf("Y->bY\n");
        getNextSym();
        if(Y()==ERROR)
            return ERROR;
    }
    else if(sym == ']'){//这个地方比较关键,如果这个条件成立,因为此时已经把[取出来了
                        //所以返回到函数B就不用取这个字符了,直接到下一个字符
        arr[row+1][col]='|';
        arr[row+2][col]='ε';
        printf("Y-> \n");
    }
    else
        return ERROR;
    return 0;
}

对于如何实现输出语法树,因为我的方法有局限性,这里就不再详谈了。

运行结果如下:(图片中的?实际是ε)

在这里插入图片描述

在这里插入图片描述



版权声明:本文为m0_45892187原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。