首页 > 其他分享 >编译原理:语法分析

编译原理:语法分析

时间:2023-06-10 20:22:08浏览次数:27  
标签:case 语法分析 add0 parsingStack 编译 匹配 push 原理

实验三 语法分析

实验目的

  • 给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。
  • 通过设计、编制、调试一个典型的语法分析程序,实现对词法分析 程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用 的语法分析方法。
  • 选择一种语法分析方法(递归子程序法、LL(1)分析法、算符优先分 析法、SLR(1)分析法);选择常见程序语言都具备的语法结构,如赋 值语句,特别是表达式,作为分析对象。

主要思想:

我们使用LL(1)分析法进行语法分析。上一次实验我们已经获得了词法分析器,其结果是本次实验的输入,因此我们直接在上一次实验的基础上做改造。因为我们使用LL(1)进行语法分析,因此在本次实验中,我们首先计算出First集、Follow集、select集,然后根据select集画出预测分析表。

如下图所示,首先我们写出题目要求的最基本的文法:

s->add0
add0->add0 opt1 mul0|mul0
mul0->mul0 opt2 exp|exp
opt1->+|-
opt2->*|/
exp->(add0)|a

可以看出上述文法表示了一个四则运算的算数表达式。但是上述文法中存在诸如add0->add0的左递归,我们应当消除左递归。消除后文法如下:

s->add0
add0-> mul0 add1
add1->opt1 mul0 add1|空
mul0->exp mul1
mul1->opt2 exp mul1|空
opt1->+|-
opt2->*|/
exp->(add0)|a

根据该文法的表达式,我们可以计算三个集合,并且做出如下的预测分析表:

image-20230519143234141

在上表中,各行分别代表状态为行索引的状态时,输入为各符号时下一个状态是什么。如第一行就代表了当前状态为初始态S时,当输入为符号alpha(也就是包括0-9与变量标识符)时,状态会变为add0;当遇到 ( 时,状态会变为add0。其余以此类推。

由此,我们只需要通过程序实现该表格即可。

主要代码

  • 状态转移函数:重要的功能代码就是实现该表的状态转换,这一点用switch-case即可实现。由于该函数代码过长,截断其中三个case输出:
int stateChange(Symbol sym) { //返回状态有三种:-1代表匹配失败,0代表匹配成功且是非终结符,1代表匹配成功且是终结符
          switch (parsingStack.peek()) {
              case START:
                  switch (sym) {
                      case ALPHA:
                      case LPARN:
                          parsingStack.pop();
                          parsingStack.push(GRAMMAR.ADD0);
                          return 0;
                      default:
                          return -1;
                  }
              case ADD0:
                  switch (sym){
                      case ALPHA:
                      case LPARN:
                          parsingStack.pop();
                          parsingStack.push(GRAMMAR.ADD1);
                          parsingStack.push(GRAMMAR.MUL0);
                          return 0;
                      default:
                          return -1;
                  }
              case ADD1:
                  switch (sym) {
                      case RPARN:
                      case END:
                          parsingStack.pop();
                          parsingStack.push(GRAMMAR.BLANK);
                          return 0;
                      case OPT1:
                          parsingStack.pop();
                          parsingStack.push(GRAMMAR.ADD1);
                          parsingStack.push(GRAMMAR.MUL0);
                          parsingStack.push(GRAMMAR.OPT1);
                          return 0;
                      default:
                          return -1;
                  }
  • 匹配函数。每次状态转移后,都应该检测是否转移到“终态”,即应当匹配缓冲串和状态是否一致,一致就都弹出,重新回到初态。

    boolean check(List<Symbol> input){
            parsingStack.push(GRAMMAR.START);
            int index = 0;
            while(index<input.size()&&!parsingStack.empty()){
                if (!parsingStack.empty()) {
                    //System.out.println("现在匹配:" + index + " INPUT为:" + input.get(index).name());
                    System.out.println(parsingStack);
                }
                int flag = stateChange(input.get(index));
                if(flag==-1) {
                    System.out.println("没有可用的语法,匹配失败");
                    return false;
                }
                else if(flag==0){//状态已经转变,可以继续下一次循环
                    continue;
                }
                else //flag == 1,比较栈顶和输入符号
                {
                    if(input.get(index).name().equals(parsingStack.peek().name())) {//说明栈顶和INPUT字符相等,可以消去
                        parsingStack.pop();
                        index++;
                    }
                    else {
                        System.out.println("栈顶和INPUT字符不相等,匹配失败");
                        return false;
                    }
                }
    
            }
            if(parsingStack.empty()&&index==input.size()-1&&input.get(index).name()=="END")
            {
                System.out.println("匹配成功");
                return true;
            }
            else
            {
                System.out.println("未同时为空,匹配失败");
                return false;}
    
        }
    

运行结果

测试样例:

3 + 5
a + 1 + 5+ num
12+23*3
((((1+1)*a)*4)/4)+4+2
a +
a
+
(3+3)*(4 +
+ 4 +s

运行结果:

匹配成功
accept
匹配成功
accept
匹配成功
accept
匹配成功
accept
没有可用的语法,匹配失败
deny
匹配成功
accept
没有可用的语法,匹配失败
deny
没有可用的语法,匹配失败
deny
没有可用的语法,匹配失败
deny

可以看到运行结果正确。

标签:case,语法分析,add0,parsingStack,编译,匹配,push,原理
From: https://www.cnblogs.com/czy-blogs/p/17471881.html

相关文章

  • 大二上 | 计算机组成原理 · 小测试卷
    一共有两张图片:(任国林老师:听我说谢谢你......
  • mysql 主从复制 原理
     mysql主从复制定义mysql主从复制是一种数据同步的技术,它可以让一个或多个从数据库(slave)复制主数据库(master)的数据变化。这样可以提高数据库的可用性、性能和扩展性,也可以实现读写分离和数据备份。mysql主从复制原理mysql主从复制的原理是基于二进制日志(binlog)的,主数据库......
  • mysql MVCC 原理
    MVCC的定义MVCC,即多版本并发控制,是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC的目的是为了提高数据库的并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。MVCC的目的在MySQL中,InnoDB......
  • DevExpress源码编译(部分翻译)
    环境准备(DevExpressv18.2~22.2):vs2015至2022版本.netframework4.7.2或更高(实际我们项目用4.5.2可以编译)asp.netmvc3(devexpressmvc项目)在devexpress安装目录下(默认C:\ProgramFiles\DevExpress(version)\Components\)创建dlls目录,复制以下依赖。Microsoft.VisualStu......
  • 地址空间以及编译模式
    Linux下32位环境的用户空间内存分布: Linux下64位环境的用户空间内存分布:前面讲到,在64位环境下,虚拟地址虽然占用64位,但只有最低48位有效。故从0000800000000000~FFFF800000000000,棕色FFFF所代表的这十六位就变成了无效区域(未定义)。 程序代码区用来保存函数体的二进制代码......
  • 关于AWS-EC2-EBS-快照-或者AMI-创建的过程及原理
    对于AWSEC2的EBS创建快照Snapshot的原理逻辑,主要如下快照是异步制作的;时间点快照是立即创建的,但在快照完成(当所有已修改数据块都已转移到AmazonS3时)之前,其状态为 pending,很多大型初始快照或后续快照(其中的数据块已更改)可能需要几个小时才能完成。执行期间,正在进行的快照......
  • Windows桌面水印去除工具Universal Watermark Disabler原理分析及实现
    1.背景  最近做驱动开发,开启了系统测试模式,于是桌面的右下角就有一个水印,如下图:  测试了网上修改注册表方法不起作用,最后找到一款工具UniversalWatermarkDisabler可以把水印去除掉。于是对其原理有些兴趣,就有了相关的分析及编程实现。2、相关分析2.1相关行为分析  ......
  • 编译原理面试题
    1、请解释编译器前端和后端的区别,并描述它们在编译过程中的职责。编译器是将高级程序语言转换为目标机器语言的软件工具。它通常由两个主要组件组成:前端和后端。编译器前端:编译器前端主要负责源代码的分析和处理。它包括以下阶段:词法分析(LexicalAnalysis):将源代码分解成标记......
  • IDEA编译和构建JavaWeb项目时,项目中没有target目录,且out目录下classes文件下main包下
    问题如下:1.我们在添加web框架时,如图:2.在添加完框架,和配置完Tomcat我们开始运行项目,发现没有target文件和out文件下classes文件下什么都没有原因:出现这种情况,很可能是因为未加载的模块出现在了iml文件中,导致生成taget的时候出错,进而导致out文件内class文件的......
  • 如何提取DNA【原理】
    DNA提取是一种将DNA从生物样本中分离和纯化的过程。下面是一般的DNA提取步骤:选择样本:选择包含DNA的样本,可以是细胞、组织、血液、唾液、植物材料等。细胞破碎:使用物理或化学方法将细胞破碎,以释放DNA。常见的方法包括机械破碎、冻融、酶解或化学溶解。溶解蛋白质:加入蛋白......