【编译原理】自下而上语法分析(C/C++源码+实验报告)

本文档详细介绍了使用LR(0)方法设计和实现PL/0语言的语法分析器的过程,包括文法定义、项目集规范族、DFA构造、LR(0)分析表的建立以及算法流程。实验通过词法分析器输入,进行语法检查和结构分析,对于正确表达式报告"语法正确",错误则报告"语法错误"。文中还展示了程序源码、调试数据和实验体会,强调了LR分析法在自底向上语法分析中的应用。
摘要由CSDN通过智能技术生成

1 实验目的和内容

1.1 实验目的

(1)给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。

(2)通过设计、编制、调试一个典型的自下而上语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。

(3)选择最有代表性的语法分析方法,如算符优先分析法、LR 分析法;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用YACC 生成一个自底向上的语法分析器。

1.2 实验内容

(1)已给 PL/0 语言文法,构造表达式部分的语法分析器。

(2)分析对象〈算术表达式〉的 BNF 定义如下:

<表达式> ::= [+|-]<项>{<加法运算符> <项>}

<项> ::= <因子>{<乘法运算符> <因子>}

<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’

<加法运算符> ::= +|-

<乘法运算符> ::= *|/

<关系运算符> ::= =|#|<|<=|>|>=

1.3 实验要求

(1)将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,报告“语法正确”;对于语法错误的表达式,报告“语法错误”, 指出错误原因。

(2)把语法分析器设计成一个独立一遍的过程。

(3)采用算符优先分析法或者 LR 分析法实现语法分析;或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用 YACC 生成一个自底向上的语法分析器。

2 设计思想

2.1 根据BNF描述该文法

(1)对BNF中的各对象简称如下

E:表达式

T:项

F:因子

i:ident

n:number

(2)文法如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uZrnJB24-1634780701627)(C:\Users\User\AppData\Roaming\Typora\typora-user-images\image-20211021093224575.png)]

2.2 根据文法写出LR(0)项目集规范族

项目集规范族如下所示,一共有16个状态。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

2.3 根据项目集规范族画出识别活前缀的DFA

活前缀是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。在上面的项目集中一共有四种项目,分别是归约项目、接受项目、移进项目和待约项目,根据以下两条规则来构造识别文法所以活前缀的DFA。

(1)若状态i为X->X1…Xi-1Xi…Xn,状态j为X->X1…Xi-1XiXi+1…Xn,则从状态i画一条标志为Xi的有向边到状态j;

(2)若状态i为X->α.Aβ,A为非终结符,则从状态i画一条ε边到所有状态A->.γ。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWuHuADb-1634780658940)(file:///C:\Users\User\AppData\Local\Temp\ksohtml\wpsF520.tmp.png)]

图1 识别活前缀的DFA

2.4 判断该文法是否是LR(0)文法

(1)对于I1,Follow(S)={#},与+、-不冲突;

(2)对于I2,Follow(E)={+,-,),#},与*、/不冲突;

(3)对于I12,Follow(E)={+,-,),#},与*、/不冲突。

综上,该文法不存在移进归约冲突,所以是LR(0)文法。

2.5 构造LR(0)分析表

状态ActionActionActionActionActionActionActionActionActionGotoGotoGoto
状态+-*/()in#ETF
0S4S5S6123
1S7S8acc
2r1r2S9S10r1r1
3r4r4r4r4r4r4
4S4S5S61123
5r8r8r8r8r8r8
6r9r9r9r9r9r9
7r4r5r6123
8r4r5r6133
9r4r5r614
10r4r5r615
11S7S8S16
12r2r2S9S10r2r2
13r3r3S9S10r3r3
14r5r5r5r5r5r5
15r6r6r6r6r6r6
16r7r7r7r7r7r7

3 算法流程

语法分析程序的输入是一个个单词符号的二元组,输出是一个个语法单位,它的任务是在词法分析识别出单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。

首先逐行扫描单词符号二元组,然后遍历串的每一个字符,判断是否匹配结果,再进行下一步的判断,选择对比分析表,进行归约,把对应的字母和状态压入栈中,并输出相应的语句。如果不符合这几种情况,将会归入出错处理。完整算法流程如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jiRJPqbD-1634780658943)(file:///C:\Users\User\AppData\Local\Temp\ksohtml\wpsF530.tmp.jpg)]

图2 算法流程图

4 源程序

#include<bits/stdc++.h>
using namespace std;

//构造结构体---二元组
struct Two_tuples
{
  string type;//词法分析的类型
  string value;//词法分析的值
};

string input;//全局变量定义输入
Two_tuples result[200];//存放词法分析二元组
int k = 0;//输入二元组时访问下标
bool ans = true;//存放判断结果
int fh[101],zt[101];//符号栈,状态栈

bool target_judge(string input , string str) //判断是否和目标串匹配
{
  //首先匹配长度,看长度是否一致
  if(input.length()!=str.length()) return false;
  //长度一致,逐个匹配字符
  else
  {
    for(unsigned int i=0;i<str.length();i++)
    {
      if(input[i]!=str[i]) return false;
    }
    return true;
  }
} 

void input_cf() //输入词法分析的二元组
{
  string str;
  while(cin>>str)
  {
    //定义指针访问
    const char *d = "(),";
    char *p;
    //临时存放字符串,便于分割处理
    char buf[101];
    strcpy(buf,str.c_str()); //字符串转成数组
    //开始处理每个二元组
    p = strtok(buf,d);
    int i = 0;
    //把输入的二元组存储到结构体中
    while(p)
    {
      if(i%2==0) result[k].type = p;
      else result[k].value = p;
      i++;
      p = strtok(NULL,d);
    }
    k++;
  }
  result[k].type = "#";
}
 

//在LR分析表中判断是哪个目标串(0--8)
int chart(string str)
{
  if(target_judge("plus",str)) return 0;
  else if(target_judge("minus",str)) return 1;
  else if(target_judge("times",str)) return 2;
  else if(target_judge("slash",str)) return 3;
  else if(target_judge("lparen",str)) return 4;
  else if(target_judge("rparen",str)) return 5;
  else if(target_judge("ident",str)) return 6;
  else if(target_judge("number",str)) return 7;
  else if(target_judge("#",str)) return 8;
  else return -1;
} 

//ETF归约(9--11)
int gy_1(int num)
{
  //E---表达式
  if(num - 100 == 1) return 9;
  else if(num - 100 == 2) return 9;
  else if(num - 100 == 3) return 9;
  //T---项
  else if(num - 100 == 4) return 10;
  else if(num - 100 == 5) return 10;
  else if(num - 100 == 6) return 10;
  //F---因子
  else if(num - 100 == 7) return 11;
  else if(num - 100 == 8) return 11;
  else if(num - 100 == 9) return 11; 
  else return -1;
} 

//gy_1中规约后约几项
int gy_2(int num)
{
  //E---表达式
  if(num - 100 == 1) return 1;
  else if(num - 100 == 2) return 3;
  else if(num - 100 == 3) return 3;
  //T---项
  else if(num - 100 == 4) return 1;
  else if(num - 100 == 5) return 3;
  else if(num - 100 == 6) return 3;
  //F---因子
  else if(num - 100 == 7) return 3;
  else if(num - 100 == 8) return 1;
  else if(num - 100 == 9) return 1; 
  else return -1;
} 

int main()
{
  input_cf(); //输入词法分析结果
  //LR分析表,其中大于100为归,小于100为移进
  int LR_chart[17][12]={{0,0,0,0,4,0,5,6,0,1,2,3},
                        {7,8,0,0,0,0,0,0,100,0,0,0},
                        {101,101,9,10,0,1,0,0,101,0,0,0},
                        {104,104,104,104,0,104,0,0,104,0,0,0},
                        {0,0,0,0,4,0,5,6,0,11,2,3},
                        {108,108,108,108,0,108,0,0,108,0,0,0},
                        {109,109,109,109,0,109,0,0,109,0,0,0},
                        {0,0,0,0,4,0,5,6,0,0,12,3},
                        {0,0,0,0,4,0,5,6,0,0,13,3},
                        {0,0,0,0,4,0,5,6,0,0,0,14},
                        {0,0,0,0,4,0,5,6,0,0,0,15},
                        {7,8,0,0,0,16,0,0,0,0,0,0},
                        {102,102,9,10,0,102,0,0,102,0,0,0},
                        {103,103,9,10,0,103,0,0,103,0,0,0},
                        {105,105,105,105,0,105,0,0,105,0,0,0},
                        {106,106,106,106,0,106,0,0,106,0,0,0},
                        {107,107,107,107,0,107,0,0,107,0,0,0}}; 
  int i = 0 , j = 0; //访问栈下标
  fh[0] = 8 , zt[0] = 0;//初值
  while(LR_chart[zt[j]][chart(result[i].type)]!=100)
  {
    //判断错误
    if(LR_chart[zt[j]][chart(result[i].type)]==0)
    {
      ans = false;
      break;
    }
    //移进
    else if(LR_chart[zt[j]][chart(result[i].type)]<100)
    {
      j++;
      zt[j] = LR_chart[zt[j-1]][chart(result[i].type)];
      fh[j] = chart(result[i].type);
      i++;
    }
    //归约
    else
    {
      int res = LR_chart[zt[j]][chart(result[i].type)];
      j -= gy_2(res);
      j++;
      //移入符号栈,状态栈
      fh[j] = gy_1(res);
      zt[j] = LR_chart[zt[j-1]][gy_1(res)];
      //判断错误
      if(zt[j]==0)
      {
        ans = false;
        break;
      }
    }
  }
  if(ans == true) cout<<"Yes,it is correct."<<endl;
  else cout<<"No,it is wrong."<<endl;
  return 0;
}

5 调试数据

(1)测试样例如下

【样例输入】
(lparen,()
(ident,a)
(plus,+)
(number,15)
(rparen,))
(times,*)
(ident,b)

【样例输出】
Yes,it is correct.

(2)测试样例结果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pvqlrfw-1634780658947)(file:///C:\Users\User\AppData\Local\Temp\ksohtml\wpsF551.tmp.png)]

图3 样例测试结果

6 思考:词法分析+语法分析

6.1 将实验一的词法分析作为函数写入语法分析程序

int main()
{
  string st1r;
  //输入源程序
  for(int i=0;i<L-1;i++)
  {
    cin>>str1;
    //检测输入的结束
    string str;
    str = fun_cifa(str1);//调用词法分析子程序
}
input_cf(); //处理词法分析结果
  //LR分析表,其中大于100为归,小于100为移进
  int LR_chart[17][12]={{0,0,0,0,4,0,5,6,0,1,2,3},
                        {7,8,0,0,0,0,0,0,100,0,0,0},
                        {101,101,9,10,0,1,0,0,101,0,0,0},
                        {104,104,104,104,0,104,0,0,104,0,0,0},
                        {0,0,0,0,4,0,5,6,0,11,2,3},
                        {108,108,108,108,0,108,0,0,108,0,0,0},
                        {109,109,109,109,0,109,0,0,109,0,0,0},
                        {0,0,0,0,4,0,5,6,0,0,12,3},
                        {0,0,0,0,4,0,5,6,0,0,13,3},
                        {0,0,0,0,4,0,5,6,0,0,0,14},
                        {0,0,0,0,4,0,5,6,0,0,0,15},
                        {7,8,0,0,0,16,0,0,0,0,0,0},
                        {102,102,9,10,0,102,0,0,102,0,0,0},
                        {103,103,9,10,0,103,0,0,103,0,0,0},
                        {105,105,105,105,0,105,0,0,105,0,0,0},
                        {106,106,106,106,0,106,0,0,106,0,0,0},
                        {107,107,107,107,0,107,0,0,107,0,0,0}}; 
  int i = 0 , j = 0; //访问栈下标
  fh[0] = 8 , zt[0] = 0;//初值
  while(LR_chart[zt[j]][chart(result[i].type)]!=100)
  {
    //判断错误
    if(LR_chart[zt[j]][chart(result[i].type)]==0)
    {
      ans = false;
      break;
    }
    //移进
    else if(LR_chart[zt[j]][chart(result[i].type)]<100)
    {
      j++;
      zt[j] = LR_chart[zt[j-1]][chart(result[i].type)];
      fh[j] = chart(result[i].type);
      i++;
    }
    //归约
    else
    {
      int res = LR_chart[zt[j]][chart(result[i].type)];
      j -= gy_2(res);
      j++;
      //移入符号栈,状态栈
      fh[j] = gy_1(res);
      zt[j] = LR_chart[zt[j-1]][gy_1(res)];
      //判断错误
      if(zt[j]==0)
      {
        ans = false;
        break;
      }
    }
  }
  if(ans == true) cout<<"Yes,it is correct."<<endl;
  else cout<<"No,it is wrong."<<endl;
  return 0;
}

6.2 调试数据

【样例输入】
(a+15)*b 

【样例输出】
(lparen,()
(ident,a)
(plus,+)
(number,15)
(rparen,))
(times,*)
(ident,b)
Yes,it is correct.

6.3 调试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xqRp6dLK-1634780658951)(file:///C:\Users\User\AppData\Local\Temp\ksohtml\wpsF571.tmp.png)]

图4 样例测试结果

7 实验调试情况及体会

7.1 实验调试情况

由上两步中的测试样例可以得到,所有的测试样例均得到了相应的输出结果,说明代码编写成功,并且在代码中设置了出错处理,以便解决其他情况。

本次实验同时也实现了词法分析与语法分析合在一起去识别源程序的程序,但依然存在一些问题,比如语句只能一句一句地去识别,而不能进行整体识别,该问题会在后续过程中加以解决。

7.2 实验体会

这次实验采用的方法是自下而上分析中的LR分析法,在做实验之前,一直考虑是用算符优先分析还是LR分析来写,最后还是决定用LR分析来写。LR分析算法比较难算的地方是构造LR分析表,需要首先画出项目集规范族和DFA,需要注意的是要认真写出每个项目集规范族,以防错了一个会影响整个结果。再三检查后,才要开始按照规则构造ACTION和GOTO 分析表。分析表也需要多次核实,如果写错一个,在归约过程中就会出错。

在写代码的时候,由于归约和移进是通过s和r进行区分,于是我想到如果归约的话就加100,而acc就用100来表示,减少判断,给编码带来了方便。在判断结束之前,只有两个动作分别为移进和规约,然后分别对这两个过程进行处理就可以。

因为自己完成代码比较早,在当时上实验课时老师说道分析表是不是可以用代码来构造,算符分析表构造可能相对容易,但LR分析表中构造DFA就比较麻烦,根据DFA构造分析表的过程不算困难。

通过这次的实验,自己回顾了算符分析和LR分析的具体方法,也计算了老师ppt里的几道练习题,对于自下而上的语法分析过程进一步的理解也通过这次实验加强了自己处理字符串和运用栈来解决问题的能力。对编译原理的语法分析过程有了更好的理解,为下一次语义分析奠定基础。

最后,感谢刘善梅老师和其他同学在这次实验中给予我的帮助,不胜感激!

附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析. C2.2 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析. C2.2.1待分析的简单语言的语法 实验目的 通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法. 实验要求 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x:=(a+b)/c end# 输出的三地址指令如下: (1) t1=3*4 (2) t2=2+t1 (3) a=t2 (4) t3=a+b (5) t4=t3/c (6) x=t4 算法思想 1设置语义过程 (1) emit(char *result,char *arg1,char *op,char *ag2) 该函数功能是生成一个三地址语句送到四元式表中。 四元式表的结构如下: struct {char result[8]; char ag1[8]; char op[8]; char ag2[8]; }quad[20]; (2)char *newtemp() 该函数回送一个新的临时变量名,临时变量名产生的顺序为T1,T2,…. Char *newtemp(void) { char *p; char m[8]; p=(char *)malloc(8); k++; itoa(k,m,10); strcpy(p+1,m); p[0]=’t’; return(p); } (2)主程序示意图如图c.10所示。 (2) 函数lrparser在原来语法分析的基础上插入相应的语义动作:将输入串翻译成四元式序列。在实验中我们只对表达式、赋值语句进行翻译。 语义分析程序的C语言程序框架 int lrparser() { int schain=0; kk=0; if(syn=1) { 读下一个单词符号; schain=yucu; /调用语句串分析函数进行分析/ if(syn=6) { 读下一个单词符号; if(syn=0 && (kk==0)) 输出(“success”); } else { if(kk!=1 ) 输出 ‘缺end’ 错误;kk=1;} else{输出’begin’错误;kk=1;} } return(schain); int yucu() { int schain=0; schain=statement();/调用语句分析函数进行分析/ while(syn=26) {读下一个单词符号; schain=statement(); /调用语句分析函数进行分析/ } return(schain); } int statement() { char tt[8],eplace[8]; int schain=0; {switch(syn) {case 10: strcpy(tt,token); scanner(); if(syn=18) {读下一个单词符号; strcpy(eplace,expression()); emit(tt,eplace,””,””); schain=0; } else {输出’缺少赋值号’的错误;kk=1; } return(schain); break; } } 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 or 14) { 操作符 tt= ‘+’或者‘—’; 读下一个单词符号; strcpy(ep2,term());/调用term分析产生表达式计算的第二项ep2/ strcpy(tp,newtemp());/调用newtemp产生临时变量tp存储计算结果/ emit(tp,eplace,tt,ep2);/生成四元式送入四元式表/ strcpy(eplace,tp); } return(eplace); } char *term(void)/仿照函数expression编写/ char *factor(void) {char *fplace; fplace=(char *)malloc(12); strcpy(fplace, “ ”); if(syn=10) {strcpy(fplace,,token);/将标识符token的值赋给fplace/ 读下一个单词符号; } else if(syn=11) {itoa(sum,fplace,10); 读下一个单词符号; } else if (syn=27) {读下一个单词符号; fplace=expression();/调用expression分析返回表达式的值/ if(syn=28) 读下一个单词符号; else{输出‘}’错误;kk=1; } } else{输出‘(’错误;kk=1; } return(fplace); }
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小天才才

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值