PL0编译语义分析 递归

云南大学编译技术实验报告

一、实验目的
1. 进一步理解递归下降分析原理和实现方法;
2. 理解语法、语义分析程序为中心的单遍编译程序组织方法;
3. 理解语义分析的基本机制;
4. 掌握语义子程序的构造方法;
5. 理解编译程序的基本逻辑过程(词法分析、语法分析、语义分析及目标代码的生成;
6. 理解编译过程中的符号表的基本管理方法;

二、实验内容
文法G[E]如下:
E→E+T | E-T |T
T→T*F | T/F | F
F→( E )
F→ id
注 id 为字母打头后面是数字或者字母的串;
在starter files基础上
1. 实现增加乘法 * 和除法 / 运算,并且,/优先级别高于+;其次,和/满足左结合规则;
2. 对包含乘除法运算的表达式生成对应的四元式;
3. 禁止同名重复声明,所以登记符号之前要检查有没有同名变量声明过;
4. 增加类型检查;

三、实验的分析与设计
1.经过修改后的翻译模式:
start-> DS.
D->B; D
D->ε
B->int L { L.type := int }|real L { L.type := real }
L->id { A.Type := L.type enter(v.entry,L.type)} A
A-> ,idA { A1.Type := A.type enter(v.entry,A.type)}
A->ε
S→ V := E { gen( “:=”, E.place,0,V.place) } H
H→;S | ε
E->T { R.i:=T.place} R {E.place:=R.s}
R->+T { R1.i:= newtemp; gen( “*”, R.i, T.place , R1.i) } R {R.s:= R1.s; }
R-> ε {Rs=R.i}

T→F {p.i:=F.place} P {T.place:=P.s}
P->F {p1.i:=newtemp;gen(““,P.i,T.place,P.i)} P {P.s:=P1.s;}
P->/F {p1.i:=newtemp;gen(“/”,P.i,T.place,P.i)} P {P.s:=P1.s;}
P-> ε {Ps=P.i}

F->( E ) { F.place := E.place}
F->id {F.place:=position (id)}
V->id {V.place:=position(id)}

type、i属性为继承属性,place、s属性为综合属性。

  1. 实现增加乘法 * 和除法 / 运算,并且,/优先级别高于+;其次,和/满足左结合规则;
  2. 对包含乘除法运算的表达式生成对应的四元式;
    E->T { R.i:=T.place} R {E.place:=R.s}
    R->+T { R1.i:= newtemp; gen( “*”, R.i, T.place , R1.i) } R {R.s:= R1.s; }
    R-> ε {Rs=R.i}

T→F {p.i:=F.place} P {T.place:=P.s}
P->F {p1.i:=newtemp;gen(““,P.i,T.place,P.i)} P {P.s:=P1.s;}
P->/F {p1.i:=newtemp;gen(“/”,P.i,T.place,P.i)} P {P.s:=P1.s;}
P-> ε {Ps=P.i}
3. 禁止同名重复声明,所以登记符号之前要检查有没有同名变量声明过;
登记符号之前要检查有没有同名变量声明过。
在enter( )这个存放声明变量的函数中,增加相应检验变量名重复的函数:

  1. 增加类型检查;
    检验’:=’前后字符类型是否匹配

检验’+’前后类型是否匹配

检验’*’前后的类型是否匹配

检验’/’前后的类型是否匹配

四、实验的实现

五、运行的结果以及存在问题及其原因

#include <stdio.h>
#include<dos.h>
#include<stdlib.h>
#include<string.h>
//#include <iostream.h>
#include<ctype.h>
#include <windows.h>

#define txmax 100    /* 符号表最大容量 */
#define al 10        /* 符号的最大长度 */
#define tvmax 500    /* 最多能够申请的临时变量取值范围[100, tvmax] */
#define norw 2       /* 关键字个数 */
#define cxmax 500   /* 最多的虚拟机代码数 */

int tx;              //名字表指针, 取值范围[0, txmax-1]
int tv ;             //临时变量计数


/* 符号 */
enum symbol {
    nul,         eeof,  ident,         plus,      times,    lparen,divide,
    rparen,    comma,     semicolon,  becomes,  period, realsym,    intsym,
};

enum symbol sym;    /* 当前的符号 */
char ch;            /* 获取字符的缓冲区,getch 使用 */
char id[al+1];      /* 当前ident, 多出的一个字节用于存放0 */
char a[al+1];       /* 临时符号, 多出的一个字节用于存放0 */

/* 符号表结构 */
struct tablestruct
{
   char name[al];      /* 名字 */
    enum symbol type;   // 类型
};

struct tablestruct table[txmax]; /* 符号表 */

char word[norw][al];        /* 保留字 */
enum symbol wsym[norw];     /* 保留字对应的符号值 */
enum symbol ssym[256];      /* 单字符的符号值,散列表 */

int cx;             /* 四元式代码指针, 取值范围[0, cxmax-1]*/
struct instruction
{
    char f[al+1]; /* 操作码 */
    char l[al+1];     /* 左操作数 */
    char r[al+1];     /* 右操作数*/
    char t[al+1];     /* 结果 */
};
struct instruction code[cxmax]; /* 存放虚拟机代码的数组 */

FILE* fin;
FILE* fout;

int getsym();
void enter(enum symbol type);
void init();
int position(char* idt);
int gen(enum symbol op, int arg1, int arg2,int result );     //中间代码分析
void writecode(char *op, char *arg1, char *arg2,char *result );    //写缓存
void  start();
void  D();
void  B();
void L(enum symbol type);
void A(enum symbol type);
void S();
void H();
int E();
int R(int Ri);
int T();
int P(int Pi);
int F();
int V();
#include "语义分析.h"

int  main()
{
    char filename[20];

    printf("请输入分析的文件名:");
    gets(filename);
    if((fin=fopen(filename,"r"))==NULL)
    {
        printf("不能打开文件.\n");
        exit(0);
    }

    init();                  //初始化
    getsym();                //读第一个单词,将单词类别放入sym中,单词值放入id中
    start();                //开始按start->DS.  分析

    if (sym==eeof)
    {
        printf("语法正确\n\n将中间代码保存到文件请输入文件名,否则回车");
        gets(filename);
        if(strlen(filename)<=0) return 0;
        if((fout=fopen(filename,"w"))==NULL)
        {
            printf("不能打开文件.\n");
            exit(0);
        }
        for (int cx1=0; cx1<cx; cx1++)
            fprintf(fout,"(%s,%s,%s,%s)\n",code[cx1].f,code[cx1].l,code[cx1].r,code[cx1].t);
        return 0;
    }
    else
    {
        printf("语法错1:  . 后不能再有句子");
        exit(0);
    }
    fclose(fin);
    fclose(fout);
    return 0;
}



//初始化函数
void init()
{
    int i;

    /* 设置单字符符号 */
    for (i=0; i<=255; i++)
    {
        ssym[i] = nul;            //不正确的单字符符号为nul,先预置初值nul
    }
    ssym['+'] = plus;
    ssym['*'] = times;
    ssym['('] = lparen;
    ssym[')'] = rparen;
    ssym['.'] = period;
    ssym[','] = comma;
    ssym[';'] = semicolon;
    ssym['/']=divide;

    /* 设置保留字名字 */
    strcpy(&(word[0][0]), "real");
    strcpy(&(word[1][0]), "int");

    /* 设置保留字符号 */
    wsym[0] = realsym;
    wsym[1] = intsym;

    tv=100;           //临时变量指针初值,让Tx和tv的取值没有交集,区别到底是临时变变量和声明的变量
    tx=0;           //表指针初值
    cx=0;     //指令计数器

}


/*
* 词法分析,获取一个符号
*/
int getsym()
{
    int i,k;
    ch=fgetc(fin);

    if (ch==EOF)
    {
        sym=eeof;    //文件结束
        return 0;
    }

    while (ch==' ' || ch==10 || ch==13 || ch==9)  /* 忽略空格、换行、回车和TAB */
        ch=fgetc(fin);

    if (ch>='a' && ch<='z')
    {
        /* 名字或保留字以a..z开头 ,*/
        k = 0;
        do
        {
            if(k<al)                  /* 符号的最大长度为al ,超长就截尾处理*/
            {
                a[k] = ch;
                k++;
            }
            ch=fgetc(fin);
        }
        while ((ch>='a' && ch<='z' )|| (ch>='0' && ch<='9'));

        a[k] = 0;
        strcpy(id, a);//a的地址赋予id
        fseek(fin,-1,1);//文件指针偏移,向后读一个

        for (i=0; i<norw; i++)      /* 搜索当前符号是否为保留字 */
            if (strcmp(id,word[i]) == 0)//如果是
                break;
        if (i <norw)//已经经过定义,不可作为变量和过程名
        {
            sym = wsym[i];
        }
        else
        {
            sym = ident;          /* 搜索失败则,类型是标识符 */
        }
    }
    else if(ch == ':') /* 检测赋值符号 */
    {
        ch=fgetc(fin);
        if (ch == '=')
        {
            sym = becomes;
        }
        else
        {
            sym = nul;  /* 不能识别的符号 */
        }
    }
    else sym = ssym[ch];     /* 当符号不满足上述条件时,全部按照单字符符号处理 */
    return 0;
}


/*
* 在符号表中加入一项
*/

void enter(enum symbol type)//将声明过的变量保存在结构体table[tx]中
{
    tx=tx+1;
    if (tx > txmax)
    {
        printf(" 符号表越界 ");           /* 符号表越界 */


        return;
    }
    for(int i=1; i<txmax; i++)
    {
        if(strcmp(id,table[i].name)==0)
        {
            printf("错误1:变量名重复定义\n");
            exit(0);
        }
    }
    strcpy(table[tx].name, id); /* 全局变量id中已存有当前名字的名字,Tx为插入当前符号之前表尾指针 */
    table[tx].type = type;

}



/*
* 查找名字的位置.
* 找到则返回在名字表中的位置,否则返回0.
*
* idt:    要查找的名字
* tx:     当前名字表尾指针,全局变量
*/
/*判断是否已经存在*/
int position(char* idt)
{
    int i;
    strcpy(table[0].name, idt);
    i = tx;
    while (strcmp(table[i].name, idt) != 0)
    {
        i--;
    }
    return i;
}


/* 中间代码,生成四元式,目标代码*/
int gen(enum symbol op, int arg1, int arg2,int result )//通过传来的参数产生四元式
{
    char temp1[al+1],temp2[al+1],temp3[al+1];
    if(arg1>=100)                            //模拟申请临时变量
    {
        wsprintf(temp1,"T%d",arg1);//temp1是一个缓冲区,将arg1内容输出到temp1中
    }
    else
    {
        strcpy(temp1, table[arg1].name);//把table[arg1].name的内容copy到temp1中
    }

    if(arg2>=100)
    {
        wsprintf(temp2,"T%d",arg2);
    }
    else
    {
        strcpy(temp2, table[arg2].name);
    }

    if(result>=100)
    {
        wsprintf(temp3,"T%d",result);
    }
    else
    {
        strcpy(temp3,table[result].name);
    }

    if (op==becomes)//赋值符
    {
        if(table[arg1].type!=table[result].type) //检验':='前后字符类型是否匹配
        {
            printf("(:=,%s,%s,%s)\n",temp1,temp2,temp3);//输出四元式
            writecode(":=",temp1,temp2,temp3);//写入缓存
            printf("语法错:%s和%s的类型不一致\n",temp1,temp3);//':='前后字符类型不匹配,报错

        }
        else //:=前后字符类型匹配
        {
            printf("(:=,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode(":=",temp1,temp2,temp3);//写入缓存

        }
    }

    else if (op==plus)                          //+运算
    {

        if(table[arg1].type==table[arg2].type)//检验'+'前后类型是否匹配
        {
            table[result].type=table[arg1].type;
            writecode("+",temp1,temp2,temp3);
            printf("(+,%s,%s,%s)\n",temp1,temp2,temp3);
        }

        else
        {
            printf("(+,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode("+",temp1,temp2,temp3);
            printf("语法错:%s和%s的类型不一致\n",temp1,temp2);
        }
    }

    /*乘法*/
    else if(op==times)
    {
        if(table[arg1].type==table[arg2].type)//检验'*'前后的类型是否匹配
        {
            table[result].type=table[arg1].type;
            printf("(*,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode("*",temp1,temp2,temp3);
        }
        else
        {
            printf("(*,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode("*",temp1,temp2,temp3);
            printf("语法错:%s和%s的类型不一致\n",temp1,temp2);

        }

    }

    /*除法*/
    else if(op==divide)
    {
        if(table[arg1].type==table[arg2].type)//检验'/'前后的类型是否匹配
        {
            table[result].type=table[arg1].type;
            printf("(/,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode("/",temp1,temp2,temp3);
        }
        else
        {
            printf("(/,%s,%s,%s)\n",temp1,temp2,temp3);
            writecode("/",temp1,temp2,temp3);
            printf("语法错:%s和%s的类型不一致\n",temp1,temp2);

        }
    }
    return 0;
}

//处理代码段,打印
void writecode(char op[al+1], char arg1[al+1], char arg2[al+1],char result[al+1] )
{
    if (cx >= cxmax)
    {
        printf("Program too long"); /* 程序过长 */
        return ;
    }
    strcpy(code[cx].f, op);
    strcpy(code[cx].l,arg1);
    strcpy(code[cx].r,arg2);
    strcpy(code[cx].t,result);
    cx++;
    return ;
}


/*分析产生式     X->DS.   */
void  start()
{
    if (sym==intsym||sym==realsym)//如果变量是int或者real型
    {
        D();
        S();
        if (sym==period)
        {
            getsym();//调用词法分析
            return;
        }
        else
        {
            printf("语法错2: 缺少程序结束.");
            exit(0);
        }
    }
    else
    {
        printf("语法错3: 程序只能用int,和real开始,而且区分大小写");
        exit(0);
    }
}

/*递归下降分析
D->  B; D
D->ε

*/
void  D()
{
    if (sym==intsym ||sym==realsym)
    {
        B();
        if (ch=';')
        {
            getsym();
            D();
        }
        else
        {
            printf("语法错4");
            exit(0);
        }
    }
    else if(sym==ident)  return;
    else
    {
        printf("语法错5");
        exit(0);
    }
}

/*
B->   int L    { L.type := int }|real L    { L.type := real }
*/
void  B()
{
    if (sym==intsym )//继承L的类型
    {
        getsym();
        L(intsym);
    }
    else if (sym==realsym)
    {
        getsym();
        L(realsym);
    }
    else
    {
        printf("语法错6:变量不是real或int型\n");
        exit(0);
    }
}


/*
L->   id   { A.Type := L.type  enter(v.entry,L.type)}   A       V.entry通过全局变量tx隐性传递
有参,有几个继承属性就有几个参数
*/
void L(enum symbol type)//L的id赋予A
{
    int Vplace;
    if (sym==ident)
    {
        Vplace=position(id);
        if(Vplace>0)
        {
            printf("语法错7:变量重复声明\n");
            exit(0);
        }
        enter(type);
        getsym();
        A(type);
    }
    else
    {
        printf("语法错8:缺少变量");
        exit(0);
    }
}


/*
A-> ,id  A   { A1.Type := A.type  enter(v.entry,A.type)}
A->ε
*/

void A(enum symbol type)
{
    int Vplace;
    if (sym==comma)          //当前单词为,
    {
        getsym();
        if (sym==ident)
        {
            Vplace=position(id);
            if(Vplace>0)
            {
                printf("语法错7:变量重复声明\n");
                exit(0);
            }
            enter(type);
            getsym();
            A(type);
        }
        else
        {
            printf("语法错9");
            exit(0);
        }
    }
    else if (sym== semicolon)   return ;//当前单词为;即A的follow集元素相当于进行A->ε
    else
    {
        printf("语法错10");
        exit(0);
    }
}



/*
S→ V := E { gen( ":=", E.place,0,V.place) } H
*/
void S()
{
    int vplace,Eplace;
    if (sym==ident)
    {
        vplace=V();
        //getsym();
        if (sym==becomes)     //当前单词为:=
        {
            getsym();
            Eplace=E();
            gen(becomes,Eplace,-1,vplace);
            H();
        }
        else
        {
            printf("语法错11");
            exit(0);
        }
    }
    else
    {
        printf("语法错12");
        exit(0);
    }
}

/*
H→;S | ε
*/
void H()//执行下一条运算
{
    if (sym==semicolon)          //当前单词为indent类型
    {
        getsym();
        S();
    }
    else if (sym==period)   return ;
    else
    {
        printf("语法错13");
        exit(0);
    }
}
/*
E->T      { R.i:=T.place}      R     {E.place:=R.s}
综合属性,有返回值
*/

int E()//分析赋值号后面的运算
{
    int ri,tplace,Rs;
    if (sym==ident || sym== lparen)//当前为标识符或左括号
    {
        tplace=T();
        ri=tplace;
        Rs=R(ri);
    }
    else
    {
        printf("语法错14");
        exit(0);
    }
    return Rs;
}

/*
R->+T     { R1.i:= newtemp; gen( "*", R.i, T.place , R1.i) }     R   {R.s:= R1.s; }
R-> ε    {R.s=R.i}
R既有参数又有返回值
*/

int R(int Ri)//检验+运算
{
    int Rs,tplace;
    if (sym==plus)
    {
        getsym();
        tplace=T();
        tv=tv+1;            //生成临时变量
        gen(plus,Ri,tplace,tv);
        Rs=R(tv);
    }
    else if (sym== semicolon || sym==rparen|| sym==period)
    {
        Rs=Ri;
    }
    else
    {
        printf("语法错15");
        exit(0);
    }
    return Rs;
}

/*实现乘除的左结合及优先级
F->( E )  { F.place := E.place}
F->id     {F.place:=position (id)}
*/
int F()//实现*、/运算的优先级高于+运算
{
    int Fplace;
    if(sym==ident)
    {
        Fplace=position(id);//获取当前位置
        if(Fplace==0)
        {
            printf("变量没有声明\n");
            exit(0);
        }
        getsym();
    }
    else if(sym==lparen)
    {
        getsym();
        Fplace=E();
        if(sym==rparen)getsym();
        else
        {
            printf("语法错16,缺)");
            exit(0);
        }
    }

    else
    {
        printf("语法错17,,缺(");
        exit(0);
    }
    return Fplace;
}

/*
P->*F  {p1.i:=newtemp;gen("*",P.i,T.place,P.i)}  P  {P.s:=P1.s;}
P->/F  {p1.i:=newtemp;gen("/",P.i,T.place,P.i)}  P  {P.s:=P1.s;}
P-> ε    {Ps=P.i}
*/
int P(int Pi)//检验*、/运算
{
    int ps,Fplace;
    if(sym==times)
    {
        getsym();
        Fplace=F();
        tv=tv+1;//生成临时变量
        gen(times,Pi,Fplace,tv);
        ps=P(tv);
    }

    else if(sym==divide)
    {
        getsym();
        Fplace=F();
        tv=tv+1;
        gen(divide,Pi,Fplace,tv);
        ps=P(tv);
    }

    else if(sym==plus||sym==semicolon||sym==rparen||sym==period)
        //求出P的follow集,进行语法检查,如判断左括号是否匹配
    {
        ps=Pi;
    }

    else
    {
        printf("语法错18:缺少变量或(");
        exit(0);
    }

    return ps;
}
/*
将T的位置指向新增加乘除
T→F  {p.i:=F.place}   P   {T.place:=P.s}
*/
int T()
{
    int Pi,Fplace,Ps;
    if(sym==ident||sym==lparen)
    {
        Fplace=F();
        Pi=Fplace;
        Ps=P(Pi);
    }

    else
    {
        printf("语法错19");
        exit(0);
    }
    return Ps;
}

/*

V->id     {V.place:=position(id)}
*/
int V()
{
    int Vplace;
    if (sym==ident)
    {
        Vplace=position (id);
        if (Vplace==0)
        {
            printf("变量没有声明");
            exit(0);
        }
        getsym();
    }
    else
    {
        printf("语法错20");
        exit(0);
    }
    return Vplace;
}
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pl0的语法分析编译原理实验; 用C写的。 /语法分析过程中 /利用词法分析的结果进行分析 /严格按照PL0程序定义来编写 / /<程序> ::= <程序首部> <分程序>. /<程序首部> ::= PROGRAM <标识符>; /<分程序> ::= [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句部分> /<常量说明部分> ::= CONST <常量定义>{,<常量定义>}; /<常量定义> ::= <标识符>=<无符号整数> /<变量说明部分> ::= VAR <标识符>{,<标识符>}; /<过程说明部分> ::= <过程首部>;<分程序>;【原课件中没有最后的分号,经分析应该有分号】 /<过程首部> ::= PROCEDURE <标识符> /<语句部分> ::= <语句>|<复合语句> /<复合语句> ::= BEGIN <语句>{;<语句>} END【符合语句应该注意的是,END前距离END最近的那条语句一定没有分号,其他语句必须有分号】 /<语句>::= <赋值语句>|<条件语句>|<当型 循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句> /<赋值语句> ::= <标识符>:=<表达式> /<读语句> ::= READ(<标识符>{,<标识符>}) /<写语句> ::= WRITE(<表达式>{,<表达式>}) /<过程调用语句> ::= CALL <标识符>【原课件中有分号,实际不应该有】 /<条件语句> ::= IF <条件> THEN <语句> /<当型循环语句> ::= WHILE <条件> DO <语句> /<因子> ::= <标识符>|<常量>|(<表达式>) /<项> ::= <因子>{<乘法运算符><因子>} /<乘法运算符> ::= *|/ /<表达式> ::= [+|-]<项>{<加法运算符><项>} /<加法运算符> ::= +|- /<条件> ::= <表达式><关系运算符><表达式>|ODD <表达式> /<关系运算符> ::= #|=|>|>=|<|<=
编译原理实验三是通过对PL/0语言进行语义分析的实践。PL/0是一种简单的过程式语言,语法规则相对简单,适合用来学习编译原理中的语义分析语义分析编译过程中的一个重要环节,其主要任务是对源程序中的语言结构进行分析,生成相应的中间代码,为后续的优化和代码生成做准备。在PL/0语义分析的实验中,我们需要实现对PL/0语言的语义规则进行分析,包括语法树的构建、类型检查和作用域分析等内容。 首先,需要构建语法树,将源程序转换为一棵树状结构,便于后续的语义分析和中间代码生成。在构建语法树的过程中,需要对语法规则进行递归下降分析,识别各种语法结构,并生成对应的语法树节点。 其次,需要进行类型检查,对源程序中的标识符和表达式进行类型推导,保证类型的一致性和正确性。这包括对变量的声明和使用、运算符的操作数类型等方面的检查。 最后,需要进行作用域分析,确定各个变量和过程的作用域,并进行变量的定义和引用的合法性检查。在PL/0语义分析实验中,我们需要实现对静态作用域的支持,确保程序在运行过程中能够正确地获取和修改变量的值。 通过实验三的学习和实践,我们能够更深入地理解编译原理中的语义分析过程,掌握PL/0语言的语义规则和处理方法,为之后的编译器设计和实现打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值