词法、语法、语义分析全过程学习

(1)      设计思想

语义分析对象重点考虑经过语法分析后已是正确的语法范畴。在实验二或实验三“语法分析器”的里面添加 PL/0 语言“表达式”。部分的语义处理,输出表达式的中间代码,计算表达式的语义值。中间代码用四元式序列表示。

(2)      算法流程

源语言程序首先翻译成一种特殊形式的中间语言代码形式,并对其进行优化,然后再将它翻译成最终的目标代码。

中间代码

源程序的一种内部表示,不依赖目标机的结构,复杂性介于源语言和机器语言之间。

中间语言/中间代码的优点

1、逻辑结构清楚;

2、利于不同目标机上实现同一种语言;

3、利于进行与机器无关的优化;


源代码:

#include

#include

#include

#include

#include

struct quad
{
    char result[12];
    char ag1[12];
    char op[12];
    char ag2[12];
};

struct quad quad[30];
int count=0;
char *expression(void);
char prog[200],token[9];//function长度为8,加上'\0',所以数组最小大小应该为9
char ch;
int syn,p,m,n,sum=0;
int kk=0,k=0;
char *rwtab[6]= {"function","if","then","while","do","endfunc"};

void scaner()
{
    m=0;
    ch=prog[p++];//ch为读入第一个数据(一个新的字符串中第一个数据,并不代表整个文本第一个数据)
    for(n=0; n<8; n++) //初始化token数组
        token[n]='\0';
    while(ch==' ')
        ch=prog[p++]; //跳过空格
    if((ch>='a' && ch<='z')||(ch>='A' && ch<='Z'))//如果读入的第一个数据为字符 (字符串先存放在token数组中)
    {
        while((ch>='a' && ch<='z')||(ch>='A' && ch<='Z')||(ch>='0'&&ch<='9'))
        {
            token[m++]=ch;//暂时存放在token数组中
            ch=prog[p++];
        }//字符串
        token[m++]='\0';
        p--;//使得prog[p]被ch读入的最后一个字符
        syn=10;//用来区分输入数据类型
        for(n=0; n<6; n++)
        {
            if(strcmp(token,rwtab[n])==0)//判断读入字符串是否为程序中定义的特殊字符串
            {
                syn=n+1;//function:1,if:2,then:3,while:4,do:5,endfunc:6
                break;
            }
        }
    }
    else if(ch>='0' && ch<='9')//读入第一个数据为数字(读入为数字,直接经过sum运算,结果存放在sum中)
    {
        sum=0;
        while(ch>='0' && ch<='9')
        {
            sum=sum*10+ch-'0';//将输入数字转换成10进制数
            ch=prog[p++];
        }
        p--;//使得prog[p]被ch读入的最后一个数字
        syn=11;//若为数字,则,syn为11
    }
    else switch(ch)//读入第一个数据为运算符(运算符先存放在token数组中)
        {
        case '<':
            m=0;
            token[m++]=ch;//token[0]='<',m=1
            ch=prog[++p];//ch取下一个数据
            if(ch=='=')//<=
            {
                syn=22;
                //token[m+1]=ch;这一句的意思是token[2]=ch,错误,修改如下
                token[m++]=ch;//token[1]='='
            }
            else//<
            {
                syn=20;
                ch=prog[--p];//ch='<'
            }
            break;
        case '>':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=24;
                token[m++]=ch;
            }
            else
            {
                syn=23;
                ch=prog[--p];
            }
            break;
        case '=':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=25;
                token[m++]=ch;
            }
            else
            {
                syn=18;
                ch=prog[--p];
            }
            break;
        case '!':
            m=0;
            token[m++]=ch;
            ch=prog[++p];
            if(ch=='=')
            {
                syn=22;
                token[m++]=ch;
            }
            else syn=-1;
            break;
        case '+':
            syn=13;
            token[0]=ch;
            break;
        case '-':
            syn=14;
            token[0]=ch;
            break;
        case '*':
            syn=15;
            token[0]=ch;
            break;
        case '/':
            syn=16;
            token[0]=ch;
            break;
        case ';':
            syn=26;
            token[0]=ch;
            break;
        case '(':
            syn=27;
            token[0]=ch;
            break;
        case ')':
            syn=28;
            token[0]=ch;
            break;
        case '#':
            syn=0;
            token[0]=ch;
            break;
        default:
            syn=-1;
        }
}

/*该函数的功能是生成一个三地址语句送到四元式表中。*/
void emit(char *result,char *ag1,char *op,char *ag2)
{
    strcpy(quad[count].result,result);
    strcpy(quad[count].ag1,ag1);
    strcpy(quad[count].op,op);
    strcpy(quad[count].ag2,ag2);
    count++;
    return;
}


/*该函数回送一个新的临时变量名,临时变量名产生的顺序为t1,t2,…*/
char *newtemp()
{
//k初值为0
    char *p;
    char m[8];
    p=(char *)malloc(8);
    k++;//假设第一次调用该函数,此时k=1;
    itoa(k,m,10);//m=1;
    strcpy(p+1,m);//把m的值复制到p指针指向的地址后面一个字节开始的内存中,p[1]=1
    p[0]='t';
    return(p);
}

/*factor函数:
检查是否标识符
    如果是,调用scaner函数,
    如果不是,检查是否是数值,
        如果是,调用scaner函数,
        如果不是,检查是否是'(',
            如果不是,进行出错处理,
            如果是,调用scaner函数,再调用expression()函数,返回后检查是否是')',
                如果不是,进行出错处理
                如果是,调用scaner函数,返回*/
char *factor(void)
{
    char *fplace;
    fplace=(char *)malloc(12);
    strcpy(fplace," ");//初始化fplace
    if(syn==10)//该数据单位为字符串
    {
        strcpy(fplace,token);//将该字符串赋值给fplace
        scaner();//获取下一个数据单位
    }
    else if(syn==11)//该数据单位为数字
    {
        itoa(sum,fplace,10);//将sum的值以10进制的形式存放在fplace中
        scaner();//获取下一个数据单位
    }
    else if(syn==27)//该数据单位为(
    {
        scaner();//获取下一个数据单位
        fplace=expression();//调用expression(其实就是获取完下一个数据单位后,在factor函数中对其分析,当下一个数据单位不为(,获取下一个数据单位,且执行下一句)
        if(syn==28)//如果下一个数据单位为')'
            scaner();//直接获取下一个数据单位
        else
        {
            printf("\n')'错误");
            kk=1; //标志
        }
    }
    else
    {
        printf("\n')'错误");
        kk=1;
    }
    return (fplace);//返回数据单位的内容,如果是(,则返回(后面一个数据单位的内容
}

/*term()函数的作用是判断输入的是否是由'*''/'连接成的因式,并将对应的四元式保存并输出*/
char *term(void)
{
    char *tp,*ep2,*eplace,*tt;
    tp=(char *)malloc(12);
    ep2=(char *)malloc(12);
    eplace=(char *)malloc(12);
    tt=(char *)malloc(12);
    strcpy(eplace,factor());//调用factor,eplace为上一个数据单位的内容
    while(syn==15||syn==16)//该数据单位为'*'或者'/'
    {
        if(syn==15)//如果为'*'
        {
            tt[0]='*';
            tt[1]='\0';//tt存放该数据单位的内容
        }
        else if(syn==16)//如果为'/'
        {
            tt[0]='/';
            tt[1]='\0';//tt存放该数据单位的内容
        }
        scaner();//获取下一个数据单位
        strcpy(ep2,factor());//调用factor()函数,并将函数返回值赋值给ep2(字符串,数字,'(',出错)
        strcpy(tp,newtemp());//调用newtemp()函数,将该函数返回值赋值给tp,tp为临时变量名
        emit(tp,eplace,tt,ep2);//调用emit函数,生成一个三地址语句存放在四元式表中
        strcpy(eplace,tp);//eplace为四元式
    }
    return(eplace);
}

char *expression(void)
{
    char *tp,*ep2,*eplace,*tt;
    tp=(char *)malloc(12);
    ep2=(char *)malloc(12);
    eplace=(char *)malloc(12);
    tt=(char *)malloc(12);
    strcpy(eplace,term());//调用term函数,将四元式的内容赋值给eplace
    while(syn ==13 || syn==14) //当它不为*或者/时,接着判断是否为+或者-
    {
        if(syn==13)
        {
            tt[0]='+';
            tt[1]='\0';
        }
        else if(syn==14)
        {
            tt[0]='-';
            tt[1]='\0';
        }
        scaner();
        strcpy(ep2,term());
        strcpy(tp,newtemp());
        emit(tp,eplace,tt,ep2);
        strcpy(eplace,tp);
    }
    return(eplace);//返回对应四元式的值
}

int statement()
{
    char tt[8],eplace[8];
    int schain=0;
    switch(syn)
    {
    case 10:
        strcpy(tt,token);//若第二个数据单位的syn为10,即字符串(as1),将第二个数据单位的内容复制给tt
        scaner();//再取下一个数据单位,并获得该数据单位的syn
        if(syn==18)//第三个数据单位为'='
        {
            scaner();//再取下一个数据单位,并获得该数据单位的syn(第四个数据单位)
            strcpy(eplace,expression());//调用expression,如果为+或者-,返回对应四元式的值,如果为*或者/,则返回其四元式的值
            emit(tt,eplace," "," ");
            schain=0;
        }
        else
        {
            printf("\n缺少赋值句\n");
            kk=1;
        }
        break;
    }
    return(schain);
}

int yucu()
{
    int schain=0;
    schain=statement();//调用statement()函数,
    while(syn==26)//该数据单位为;
    {
        scaner();
        schain=statement();
    }
    return(schain);
}

int lrparser()
{
    int schain=0;
    kk=0;
    if(syn==1)//输入数据为function
    {
        scaner();//因为p停留在上一次扫描结束的地方,所以再次调用该函数ch可以获取下一个数据单位,得到该数据单位的syn
        schain=yucu();//调用yucu()函数
        if(syn==0)//判断 #
        {
            scaner();
            if(syn==-1 && kk==0)
                printf("\n ......语法,语义分析成功! \n");
        }
        else
        {
            if(kk!=0)//endfunc
            {
                printf("\n缺少endfunc\n");
                kk=1;
            }
        }
    }
    else
    {
        printf("\n缺少function\n");
        kk=1;
    }
    return(schain);
}

void main()
{
    int i;
    p=0;
    FILE *fp;

    if((fp=fopen("input.txt","r"))==NULL)
    {
        printf("\n 文件打开失败:\n");
        exit(0);
    }
    do
    {
        ch=fgetc(fp);
        prog[p++]=ch;
    }
    while(ch!='#');  //将输入字符串赋值到prog数组
    p=0; //使得ch从第一个字符开始获取
    printf("种别码 单词符号\n");
    do
    {
        scaner(); //扫描输入的数据,并作出类型判断(以字符串为单位)
        switch(syn)
        {
        case 11:
            printf("%-3d %d\n",syn,sum);
            break; //输入为数字,打印出它的syn与大小
        case -1:
            printf("词法分析失败,程序终止!\n");
            return; //输入数据不正确
        default:
            printf("%-3d %s\n",syn,token); //字符串,运算符
        }
    }
    while(syn!=0); //读入所有输入的数据(只是判断输入数据是否正确,并没有进行语法,语义分析操作)
    printf("词法分析成功,按任意键进行语法、语义分析");
    getch();//按任意键,程序往下执行
    p=0;//词法分析后,重新对ch赋值,使得ch从第一个字符开始获取
    scaner();//扫描第一个单位,判断其类型
    lrparser();//将将 第一个输入字符串 翻译为四元式
    if(kk!=0)
    {
        printf("语法分析失败,程序终止!");
        return;
    }
    printf("\n三地址指令如下:\n");
    for(i=0; i    {
        printf("%s=",quad[i].result);
        printf("%s",quad[i].ag1);
        printf("%s",quad[i].op);
        printf("%s\n",quad[i].ag2);
    }
    getch();
    return;
}

源码转载于前人博客,借鉴学习并修改部分代码。链接:点击打开链接


使用方法:

在工程目录下或与该.c文件平行目录下创建一个input.txt文件。

(举例)内容加入:function result = a * (b + c)#  endfunc 

输入可加空格,但不可回车换行。


你可能感兴趣的:(词法、语法、语义分析全过程学习)