编译原理实验报告

实验一

1、程序设计思路
  在预处理处理程序的基础上,扩充程序,增加行注释。

首先,行注释以“\”开始,以回车“\n”结束。参照星注释的源代码。其次,要区分行注释与星注释,即星注释(指的是/*    */  )注释中包含行注释,行注释无效;行注释后有星注释,星注释无效。所以设计一个标记变量p,标记行注释。

2、   实现过程

  输入的当前字符cur_c 等于''\"且old_c等于"\",如果满足这样的条件,就将 in_comment改为ture,进入行注释。并且标记p=1。继续运行,当输入字符cur_c="\n"且p=1,表示行注释离开,将in_comment改为false。

3、源程序关键代码

加粗的为自己修改的代码。

#include <fstream.h>

#include <iostream.h>

void pro_process(char *);

void main( )                                                           //测试驱动程序

{//定义扫描缓冲区

       char buf[4048]={'\0'};                                      //缓冲区清0

//调用预处理程序

       pro_process(buf);

//在屏幕上显示扫描缓冲区的内容

       cout<<buf<<endl;}

void pro_process(char *buf)                                     //预处理程序

{    ifstream cinf("source.txt",ios::in);

       int i=0,p=0;                                                           //计数器

       char old_c='\0',cur_c;                                        //前一个字符,当前字符。

       bool in_comment=false;                                    //false表示当前字符未处于注释中。

       while(cinf.read(&cur_c,sizeof(char))){        //从文件读一个字符

              switch(in_comment){

              case false:

                     if(old_c=='/' && cur_c=='*'){              //进入多行注释

                            i--;                                             //去除已存入扫描缓冲区的字符'/'

                            in_comment=true;

                     }

                     else{

                            if(old_c=='/' && cur_c=='/'){            //进入行注释

                            i--;p=1;                                     //去除已存入扫描缓冲区的字符'/'

                            in_comment=true;

                            }

                       else {

                            if(old_c=='\\' && cur_c=='\n')      //发现续行

                                   i--;                                      //去除已存入扫描缓冲区的字符'\'

                    

                                   else {

                                       if(cur_c>='A' && cur_c<='Z')//大写变小写

                                              cur_c+=32;

                                       if(cur_c =='\t' || cur_c =='\n')  //空格取代TAB换行

                                              cur_c=' ';

                                          buf[i++]=cur_c ;}}}

                     break;

              case true:

                     if((old_c=='*' && cur_c=='/'))             //离开注释

                            in_comment=false;

             if(cur_c=='\n'&&p==1)  in_comment=false;

              }//end of switch

              old_c= cur_c;                                            //保留前一个字符

       }//end of while

       buf[i++]='#';                                                    //在源程序尾部添加字符'#'

}

4、结果的输入与输出

Source.txt

Begin/*S=2*3.14*R*R+2*3.14*

R*H*/

       Real R,h,s;

       s=2*3\

14*r*(r+++h) kmoiulEnd

运行结果如下:

实验二

1、程序设计思路:

编写一个自动机(DFA)识别程序,判断一个字符序列是否为一个DFA所识别。

首先在试验一的基础上,对输入的字符序列进行预处理,然后在输入字符,看其是否能被指定的DFA识别。构造DNA状态转换矩阵,由输入的字符,确定其是否有后继状态,即是否能被识别。

2、实现过程

DFA的主要函数是: scanner(char *);

核心代码如下:

       while(buf[i]==' ')i++;

   {int cur_state=0;

       while(DFA[cur_state][col(buf[i])]){//存在后继状态

           cur_state=DFA[cur_state][col(buf[i])];//进入下一状态

              if(buf[++i]=='\0')break;//指向下一字符,判缓冲区内字符是否处理完。}

3、源程序关键代码

#include <iostream.h>

#include <fstream.h>

#include <string.h>

#include <stdlib.h>

#include <conio.h>

const short WORDLEN=20;

struct code_val{

       char code;char val[WORDLEN];

};

//单词编码表

const char code[]="{}ac=+$*,;()#";

//DFA列字符

const char COL_CHAR[]="a0=+*,;()# ";

//状态转换矩阵(DFA)

const int DFA[][11]={//strlen("a0=+*,;()# ")=11

       {1,2,3,4,5,6,7,8,9,10,0},{11,11},0,12},{0},{0,0,0,13},{0},{0},{0},{0},{0},{0},{11,11},   {0,12},{0}

};

//预处理函数原型

void pro_process(char *);

//扫描函数原型

code_val scanner(char *);

//主函数

void main()

{  char buf[4048]={'\0'};//扫描缓冲区

//预处理

       pro_process(buf);//显示buf

cout<<buf<<endl;

//单词识别

       code_val t;//临时变量

       do{

              t=scanner(buf);//调用一次扫描器获得一个单词二元式

              cout<<t.code<<'\t'<<t.val<<endl;//在屏幕显示单词二元式

       }while(t.code!='#');

       cout<<"End of lexical analysis!"<<endl;

       getch();

}

int col(char);//转换函数

char search_table(char[]);//查单词二元式编码表函数

struct code_val scanner(char *buf)//扫描函数,每调用一次,返回一个单词的二元式。

{

       static int i=0;//buf指针

       struct code_val t={'\0',"NUL"};//临时变量

       char token[WORDLEN]="";//用于拼接单词

//去除前导空格

       while(buf[i]==' ')i++;

//开始识别单词

       int cur_state=0;

       while(DFA[cur_state][col(buf[i])]){//存在后继状态

           cur_state=DFA[cur_state][col(buf[i])];//进入下一状态

              if(buf[++i]=='\0')break;//指向下一字符,判缓冲区内字符是否处理完。

       }

return t;//返回当前单词的二元式

}

//转换函数

int col(char c)

{

       if(c>='a'&&c<='z')c='a';

       if(c>='0'&&c<='9')c='0';

//     const char COL_CHAR[]="a0=+*,;()# ";

       for(int i=0;i<=(int)strlen(COL_CHAR);i++)

              if(c==COL_CHAR[i])return i;

       cout<<"Error char>"<<c<<endl;exit(0); //程序终止运行  

}//预处理函数

void pro_process(char *buf)

{

       ifstream cinf("source.txt",ios::in);

       int i=0;char old_c='\0',cur_c;//计数器,前一个字符,当前字符。

       bool in_comment=false;//状态标志,false表示当前字符未处于注释中。

       while(cinf.read(&cur_c,sizeof(char))){//从文件读一个字符

              switch(in_comment){

              case false:

                     if(old_c=='/' && cur_c=='*'){//进入注释

                            i--;//去除已存入扫描缓冲区的字符'/'

                            in_comment=true;

                     }

                     else {

                            if(old_c=='\\' && cur_c=='\n') //去除续行符'\',包括后续换行符。

                                   i--;//去除已存入扫描缓冲区的字符'\'

                            else {

                                   if(cur_c>='A' && cur_c<='Z') cur_c+=32;

                                   if(cur_c=='\t' || cur_c=='\n') cur_c=' ';//空格

                                   buf[i++]=cur_c ;}}

                     break;

              case true:

                     if(old_c=='*' && cur_c=='/')//离开注释

                            in_comment=false;

              }//end of switch

              old_c= cur_c;//保留前一个字符

       }//end of while

       buf[i]='#';}

4、结果的输入与输出

输入内容在source.txt中,如下:

egin/*S=2*3.14*R*R+2*3.14*R*H*/

       Real R,h,s;

       s=2*3\

14*r*(r+++h) kmoiul

End

运行结果如下:

{     NUL

c      NUL

i      r

,      NUL

i      h

,      NUL

i      s

;      NUL

i      s

=     NUL

x     2

*     NUL

x     314

*     NUL

i      r

*     NUL

(      NUL

i      r

$     NUL

+     NUL

i      h

)      NUL

i      kmoiul

}     NUL

#     NUL

实验三

1、程序设计思路

在自上而下预测分析控制程序的基础上,根据文法规则进行语法分析,如字串是句子,写出文法的最左推导。根据表达式文法的预测分析表,对输入的符号串进行分析。输入的符号串需是单词二元式。若产生式匹配,则输出产生式。

2、实现过程

X 存放当前栈顶符号的工作单元,当X是终结符时,读入下一个符号;当X是非终结符时,判断其是否为#,若不是,则判断其M[X,a]是否是产生式(匹配),匹配则逆序出栈,然后按最左推导输出。具体代码如下:

for(i=strlen(p[M[lin(X)][col(t.code)]])-1;i>=0;i)

{stack[++top]=*(p[M[lin(X)][col(t.code)]]+i);

 cout<<X<<"->"<<p[M[lin(X)][col(t.code)]]<<endl;}

3、源程序关键代码

#include <fstream.h>

#include <iostream.h>

#include <stdlib.h>

#include <string.h>

struct code_val{

char code;char val[20];

};

const char *p[ ]={                                            //指向产生式右部符号串

"TD","+TD","","FS","*FS","","(E)","i","x","y"

};

//0.  E→TD                  D代表E'

//1.  D→+TD               

//2.  D→ε                                              

//3.  T→FS                   S代表T'

//4.  S→*FS               

//5.  S→ε

//6.  F→(E)   

//7.  F→i              

//8.  F→x                    

//9.  F→y

const char T[ ]="+*()ixy#";                               //分析表的列符

const char NT[ ]="EDTSF";                              //分析表的行符,行符为EE'TT'F。

const int M[ ][8]={       

/*在预测分析表M中存放指针数组p的下标x,设M[i][j]=x,通过p[M[i][j]]=p[x]获取右部符号串。*/

       {-1,-1,0,-1,0,0,0,-1},//p[0]指向第0个产生式右部符号串"TD",-1表示出错。

       {1,-1,-1,2,-1,-1,-1,2},

       {-1,-1,3,-1,3,3,3,-1},

       {5,4,-1,5,-1,-1,-1,5},    

       {-1,-1,6,-1,7,8,9,-1}     

};

//函数原型

int lin(char );                                                   //非终结符转换为行号

int col(char );                                                   //终结转换为列号

bool isNT(char);                                               //isNT判断是否是非终结符

bool isT(char);                                                        //isT判断是否是终结符。

void main(void)

{

       int i,j=0;

       ifstream cinf("lex_r.txt",ios::in);          //从文件lex_r.txt输入数据

       ofstream cout("par_r.txt",ios::out);       //结果输出至文件par_r.txt(cout重定义)

       struct code_val t;                                       //定义结构变量,存放单词二元式。

       cinf>>t.code>>t.val;                                  //读一个单词的二元式

       char stack[20]={'#','E'};int top=1;         //栈赋初值

       char X=' ';                                                //用于显示,并非必要。

       cout<<"step"<<'\t'<<"stack"<<'\t'<<"X"<<'\t'<<"t.code"<<endl; //用于显示,并非必要。

       cout<<j<<')';                                             //用于显示,并非必要。

       while(1){

              cout<<'\t';                                                 //用于显示,并非必要。

              for(i=0;i<=top;i++)       cout<<stack[i]; //用于显示,并非必要。

              cout<<'\t'<<' '<<'\t'<<t.code<<endl;//用于显示,并非必要。

              X=stack[top--];                                         //出栈

              cout<<++j<<')'<<'\t';                          //用于显示,并非必要。

              for(i=0;i<=top;i++)       cout<<stack[i];       //用于显示,并非必要。

              cout<<'\t'<<X<<'\t'<<t.code<<endl;     //用于显示,并非必要。

              if(X=='#'){

                     if(X==t.code){cout<<"\tAcc"<<endl;break; //跳出循环}

                     else{cout<<"Err in #>"<<X<<'\t'<<t.code<<endl;exit(0);}

              }//end of if(X=='#')

              if(isT(X)){                                               //是否是终结符

                     if (X== t.code)

                            cinf>>t.code>>t.val;             //读下一单词二元式

                     else{cout<<"Err in T>"<<X<<'\t'<<t.code<<endl;exit(0);}

                     continue;}//end of if(isT(X))

              if(isNT(X)){                                      //是否是非终结符

                     if(M[lin(X)][col(t.code)]==-1){

                            cout<<"Err in NT>"<<X<<'\t'<<t.code<<endl;exit(0);

                     }

                     else{for(i=strlen(p[M[lin(X)][col(t.code)]])-1;i>=0;i--)

                                   stack[++top]=*(p[M[lin(X)][col(t.code)]]+i);

                            cout<<X<<"->"<<p[M[lin(X)][col(t.code)]]<<endl;}

                     continue;}//end of if(isNT(X))

              cout<<"Err in main( )>"<<X<<endl;exit(0);}//end of while

}//end of main

int lin(char c)                                                  //将EDTSF分别转换为01234

{ for(int i=0;i<(int)strlen(NT);i++)

              if(c==NT[i])return i;

       cout<<"Err in lin( ) >"<<c<<endl;exit(0);}

int col(char c)                                                  //将+* ()ixy#分别转换为01234567

{or(int i=0;i<(int)strlen(T);i++)if(c==T[i])return i;

       cout<<"Err in col( )>"<<c<<endl;exit(0);}

bool isNT(char c)                                             //是否是非终结符

{for(int i=0;i<(int)strlen(NT);i++)       if(c==NT[i])return true;return false;}

bool isT(char c)                                               //是否是终结符(不包括'#')

{for(int i=0;i<(int)strlen(T)-1;i++)if(c==T[i])return true;return false;}

4、结果的输入与输出

文件lex_r.txt输入数据,如下:

i a  + nul i b # nul

结果输出至文件par_r.txt,如下:

step  stack       X     t.code

0)    #E        i

1)    #     E     i

E->TD

       #DT      i

2)    #D   T     i

T->FS

       #DSF           i

3)    #DS F     i

F->i

       #DSi            i

4)    #DS i      i

       #DS      +

5)    #D   S     +

S->

       #D        +

6)    #     D     +

D->+TD

       #DT+           +

7)    #DT +     +

       #DT      i

8)    #D   T     i

T->FS

       #DSF           i

9)    #DS F     i

F->i

       #DSi            i

10)  #DS i      i

       #DS      #

11)   #D   S     #

S->

       #D        #

12)  #     D     #

D->

       #          #

13)         #     #

       Acc

实验四

1、程序设计思路

  在LR语法分析器的控制程序的基础上,添加语义栈,在对字符串进行分析的同时,也翻译字符串。

2、实现过程

当action <0时,进行归约,action为-n,则,即归约编号为n的产生式。

若action=-1,则归约产生式E→E+T。语义栈的栈顶 yu[top]应变为当前位置与其后第二个位置的和,如下:

         case -1: yu[top]=yu[top]+yu[top+2];

同理,其余产生式的语义翻译也如下:

      case -2:yu[top]=yu[top];

         case -3:yu[top]=yu[top]*yu[top+2];

         case -4:yu[top]=yu[top];

         case -5:yu[top]=yu[top+1];

         case -6:yu[top]=yu[top];

最后,当程序接受acc之后,输出翻译结果,即输入栈顶元素。

3、源程序关键代码

#include <fstream.h>

#include <iostream.h>

#include <stdlib.h>

#include <string.h>

struct code_val{char code;char val[20];};

const char *p[]={   "S→E","E→E+T","E→T","T→T*F","T→F","F→(E)","F→i"        };

const char TNT[ ]="+*()i#ETF";                        //LR分析表列的字符

const int M[][9]={                //LR分析表数字化,列字符+*()i#ETF用数字012345678标识。

       { 0, 0, 4, 0, 5,0, 1, 2, 3},                    //0表示出错,s4用4表示。

       { 6, 0, 0, 0, 0,99},                             //Acc用99表示

       {-2, 7, 0,-2, 0,-2},                             //r2用-2表示

       {-4,-4, 0,-4, 0,-4},

    { 0, 0, 4, 0, 5, 0, 8, 2, 3},

    {-6,-6, 0,-6, 0,-6},

    { 0, 0, 4, 0, 5, 0, 0, 9, 3},

       { 0, 0, 4, 0, 5, 0, 0, 0,10},

       { 6, 0, 0,11},

       {-1, 7, 0,-1, 0,-1},

       {-3,-3, 0,-3, 0,-3},

       {-5,-5, 0,-5, 0,-5}

};

int col(char);                                                    //列定位函数原型

void main()

{int state[50]={0};                                    //状态栈初值

       int yu[50]={0};

       char symbol[50]={'#'};                               //符号栈初值

       int top=0;                                                        //栈顶指针初值

       ofstream cout("par_r.txt");                         //语法分析结果输出至文件par_r.txt

       ifstream cin("lex_r.txt");       // lex_r.txt存放词法分析结果,语法分析器从该文件输入数据。

       struct code_val t;                                       //结构变量,存放单词二元式。

       cin>>t.code>>t.val;                                          //读一单词

       int action;

       int i,j=0;                                                   //输出时使用的计数器,并非必要。

       cout<<"step"<<'\t'<<"状态栈"<<'\t'<<"符号栈"<<'\t'<<"输入符号"<<endl;//    do{

              cout<<j++<<')'<<'\t';                           //输出step,并非必要。

              for(i=0;i<=top;i++)cout<<state[i];cout<<'\t';//输出状态栈内容,并非必要。

              for(i=0;i<=top;i++)cout<<symbol[i]; //输出符号栈内容,并非必要。

              cout<<'\t'<<t.code<<endl;                                  action=M[state[top]][col(t.code)];

              if(action>0 && action!=99){               //移进

                     state[++top]=action;

                     symbol[top]=t.code;

                     yu[top]=atoi(t.val);

                     cin>>t.code>>t.val;                            //读一单词

              }

              else if(action < 0){                      //归约

              if(strcmp(p[-action]+3,"ε"))              //ε产生式的右部符号串长度为0,无需退栈。

                     top=top-(strlen(p[-action])-3);      //"→"为汉字,占二字节,故减3。

                     state[top+1]=M[state[top]][col(p[-action][0])]; //产生式左部符号

                     symbol[++top]=p[-action][0];

      switch(action)

         {

         case -1: yu[top]=yu[top]+yu[top+2];break;

         case -2:yu[top]=yu[top];break;

         case -3:yu[top]=yu[top]*yu[top+2];break;

         case -4:yu[top]=yu[top];break;

         case -5:yu[top]=yu[top+1];break;

         case -6:yu[top]=yu[top];break;

         }

}

              else if(action==99){                           //接受

                     cout<<'\t'<<"Acc"<<endl;

                     cout<<'\t'<<yu[top]<<endl;

                     break;}

              else{                                                        //出错

                     cout<<"Err in main()>"<<action<<endl;break;}}

           while(1);}

int col(char c) //将字符+* ()i#ETF分别转换为数字012345678

{     for(int i=0;i<(int)strlen(TNT);i++)

              if(c==TNT[i])return i;

       cout<<"Err in col char>"<<c<<endl;

       exit(0);                                                     }

4、结果的输入与输出

语法分析器从该文件lex_r.txt输入数据,如下:

i 3

* nul

i 3

+ nul

i 5

# nul

语法分析结果输出至文件par_r.txt,如下:

step  状态栈    符号栈    输入符号

0)    0     #     i

1)    05    #i    *

2)    03    #F   *

3)    02    #T   *

4)    027  #T*  (

5)    0274       #T*( i

6)    02745     #T*(i      +

7)    02743     #T*(F     +

8)    02742     #T*(T     +

9)    02748     #T*(E     +

10)  027486    #T*(E+   i

11)   0274865  #T*(E+i  )

12)  0274863  #T*(E+F )

13)  0274869  #T*(E+T )

14)  02748     #T*(E     )

15)  0274811  #T*(E)    #

16)  02710     #T*F       #

17)  02    #T   #

18)  01    #E   #

       Acc

       24

相关推荐