已有预测分析表时的语法分析

本文详细阐述了一种在给定预测分析表的情况下,实现编译原理中的预测分析程序的方法。通过使用结构体存储预测分析表,并提供源代码实现,作者展示了如何根据给定的产生式规则进行预测分析,特别适用于处理包含加减乘除和括号的算术表达式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇是编译原理的课堂作业实验题

  • 题目要求
    为给定文法写预测分析程序,预测分析表已知(虽然求取预测分析表更重要,但是老师要求是给定预测分析表的情况下写预测分析程序),文法如下:
    E–>TE’
    E’–> +TE’|ε
    T–>FT’
    T’–> *FT’|ε
    F–>(E) | i
    预测分析表(太难打了):
表格i+*()#
EE–>TE’E–>TE’
E’E’–> +TE’E’–>εE’–>ε
TT–>FT’T–>FT’
T’T’–>εT’–> *FT’T’–>εT’–>ε
FF–>iF–>(E)
  • 设计分析
    预测分析程序的重点应该是预测分析表的创建,但是我猜老师的意思应该是考察我们的编程能力,时间有限,我们只做老师要求的在给定预测分析表的情况下写预测分析程序,关于预测分析表的生成等内容可以在任意编译原理课本上找到,这里不再赘述。
    虽然给定预测分析表,但是编程时还是有很多东西需要考虑,首先是预测分析表用什么存储,我是用了一个Node结构体
struct Node{
    char vn;        //非终结符
    char vt[VT];    //所有终结符
    int p[VT];      //产生式规则
    Node(){
        memset(p,-1,sizeof(p));
    }
};

“vn”用于存储非终结符,“vt”数组存储所有的终结符号,在 p数组中存储预测分析表中,确定非终结符和确定终结符所需要的产生式规则,这里所找到的是产生式规则的索引,我们将产生式存放在一个字符指针数组中

const char* production[]

我们得到的索引就是对应的数组下标(这样存储在数据更多的时候可以节省很多空间)。
数据结构定义好之后,程序实现就很简单了,源代码如下:

  • 代码实现
#include<iostream>
#include<cstring>
#include<ctype.h>
#include<cstdio>
#define VT 6    //终结符号个数
#define VN 5    //非终结符号个数
#define MAXLINE 100
using namespace std;
struct Node{
    char vn;        //非终结符
    char vt[VT];    //所有终结符
    int p[VT];      //产生式规则
    Node(){
        memset(p,-1,sizeof(p));
    }
};
Node MTable[VN];
const char* production[]={"Te","Ft","i","+Te","$","*Ft","(E)"}; //所有的产生式右部
void getTable(Node *);      //获得预测分析表
void out(int cnt,const char* st,const char *tex,int j,int k,char c);   //输出当前状态
char st[MAXLINE];       //栈
int sp=0;               //栈顶指针
int main()
{
    int c,l;
    getTable(MTable);
    char tex[MAXLINE];
    memset(st,' ',sizeof(st));
    gets(tex);
    /*
    while((c=getchar())!='#'&& c!=-1)
        *(p++)=c;
    *(p++)=c;
    *p='\0';
    */

    l=strlen(tex);
    tex[l]='#'; tex[l+1]='\0';
    st[sp++]='#';
    st[sp++]='E';
    int cnt=1,i,j,k=0;
    cout<<"步骤"<<"    "<<"分析栈"<<"               "<<"剩余输入串"<<"         "<<"指导所用产生式或函数"<<endl;
    char cur,pd[MAXLINE],*pp;  //pd:production缩写,cur当前栈顶元素
    for(i=0;sp>0;cnt++){
        cur=st[--sp];
        if(st[sp]=='#'){
            st[sp+1]='\0';
            out(cnt,st,tex+i,j,-1,'#');
            continue;
        }
        if(cur==(c=tex[i])&&i<l){
            i++;
            st[sp+1]='\0';
            out(cnt,st,tex+i-1,j,-1,c);
            continue;
        }
        pp=pd;  memset(pd,0,sizeof(pd));
        for(j=0;j<VN;j++)                       //查找该非终结符对应哪个结点
            if(MTable[j].vn==cur)    break;
        if(MTable[j].vn!=cur){
            cout<<"error:不能识别输入的字符串"<<endl;
            break;
        }
        k=0;
        while(k<VT&&MTable[j].vt[k]!=c) k++;    //当前非终结符遇到该终结符对应的产生式规则
        if(k==VT) { cout<<"Error:输入有误"<<endl;    break;}
        k=MTable[j].p[k];       //重用
        st[sp+1]='\0';
        out(cnt,st,tex+i,j,k,'a');


        memcpy(pp,production[k],sizeof(production[k]));
        if(k!=4){       //产生式不为空串'$'
            int len=strlen(pp);
            while(--len>=0) st[sp++]=*(pp+len); //逆序入栈
        }
    }
    return 0;
}
void getTable(Node*t)        //获得预测分析表
{
    t[0].vn='E',t[1].vn='e',t[2].vn='T',t[3].vn='t',t[4].vn='F';    //非终结符
    for(int i=0;i<VT;i++){          //终结符
        t[i].vt[0]='i';     t[i].vt[1]='+';   t[i].vt[2]='*';
        t[i].vt[3]='(';     t[i].vt[4]=')';   t[i].vt[5]='#';
    }
    //下面用来存储相应的产生式的索引
    t[0].p[0]=0;    t[0].p[3]=0;
    t[1].p[1]=3;    t[1].p[4]=4;    t[1].p[5]=4;
    t[2].p[0]=1;    t[2].p[3]=1;
    t[3].p[1]=4;    t[3].p[2]=5;    t[3].p[4]=4;    t[3].p[5]=4;
    t[4].p[0]=2;    t[4].p[3]=6;
}
/*cnt是步骤计数,st是栈底指针,tex是剩余输入符号指针,j表示第j个Node,k表示运用的产生式
 *的索引号,如果k=-1表示一个非终结符匹配成功或者完成接受*/
void out(int cnt,const char* st,const char *tex,int j,int k,char ac)    输出当前状态
{
    char s[MAXLINE],*p,cu[3],cur=MTable[j].vn,pd[MAXLINE],*q;
    p=s;    q=pd;
    int i=0,c;
    while((c=*(st+i))!='\0'){
        if(islower(c)&&c!='i'){
            *(p++)=toupper(c);
            *(p++)='\'';
        }else
            *(p++)=c;
        i++;
    }
    *p='\0';    i=0;
    if(k!=-1){
        while((c=*(production[k]+i))!='\0'){
            if(islower(c)&&c!='i'){
                *(q++)=toupper(c);
                *(q++)='\'';
            }else
                *(q++)=c;
            i++;
        }
    }else
        *(q++)=ac;
    *q='\0';    i=0;
    if(islower(cur)&&cur!='i'){
        cu[i++]=toupper(cur);
        cu[i++]='\'';
    }else
        cu[i++]=cur;
    cu[i]='\0';
    if(k!=-1)
        printf("%4d    %-15s %15s        %-2s-->%s\n",cnt,s,tex,cu,pd);
    else if(ac!='#')
        printf("%4d    %-15s %15s        %s匹配\n",cnt,s,tex,pd);
    else
        printf("%4d    %-15s %15s        接受\n",cnt,s,tex,cu,pd);
}

这个程序可以接受所有的 + *的表达式(要求表达式的字母只能为i)如:
i+i*i、 (i+i)*(i+i)+i 等等

  • 程序测试 输入 (i+i)*(i+i)+i
    注意括号为英文符号
    这里写图片描述
    这里写图片描述

所有的函数都有我编写的时候留下的注释,应该可以看得明白,如有疑问,可以留言,力所能及 乐意解答

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值