2024-06-23 编译原理实验3——语义分析

news2024/11/26 12:13:20

文章目录

    • 一、实验要求
    • 二、实验设计
    • 三、实验结果
    • 四、附完整代码

补录与分享本科实验,以示纪念。

一、实验要求

基于前面的实验,编写一个程序对使用 C—语言书写的源代码进行语义分析,输出语义分析中发现的错误(涉及 17 种错误类型)并完成实验报告,实验中主要使用 C 语言。

  • 基本要求
    a. 对程序进行语法分析,输出语法分析结果;
    b. 能够识别多个位置的语法错误。
  • 附加要求
    a. 修改假设3在文法中加入函数声明(需要修改文法)并能够检查函数声明涉及的两种错误:函数声明了但没定义、函数多次声明冲突。
    b. 修改假设 4 在假设“变量的定义可受嵌套作用域的影响,外层语句块中定义的变量可以在内层语句块中重复定义,内层语句块中定义的变量到了外层语句块中就会消亡,不同函数体内定义的局部变量可以相互重名”。
    c. 修改前面的C–语言假设5,将结构体间的类型等价机制由名等价改为结构等价。在新的假设5下,完成对错误类型1-17的检查。

二、实验设计

一、文法可视化的建立,方便后续进行语义分析。

在这里插入图片描述
二、sematic.c文件

bool dealWithProgram(Node *node);

bool dealWithExtDefList(Node *node);

bool dealWithExtDef(Node *node);

bool dealWithSpecifier(Node *node, ValueTypes *type, char **name);

bool dealWithTYPE(Node *node, ValueTypes *type);

bool dealWithStructSpecifier(Node *node, ValueTypes *type, char **name);

bool dealWithTag(Node *node, char **name);

bool dealWithOptTag(Node *node, char **name);

bool dealWithExtDecList(Node *node, ValueTypes *type, char **name);

bool dealWithVarDec(Node *node, Symbol *s);

bool dealWithDefList(Node *node, Symbol *s);

bool dealWithDef(Node *node, Symbol *s);

bool dealWithDecList(Node *node, Symbol *s, ValueTypes *type, char **name);

bool dealWithDec(Node *node, Symbol *s, Symbol *field);

bool dealWithFunDec(Node *node, Symbol *s);

bool dealWithVarList(Node *node, Symbol *s);

bool dealWithParamDec(Node *node, Symbol *s);

bool dealWithCompSt(Node *node);

bool dealWithStmtList(Node *node);

bool dealWithStmt(Node *node);

bool dealWithExp(Node *node, ExpType *expType);

bool dealWithArgs(Node *node, ParaType *parameters);

语义分析需要对每个匹配的产生式进行分析,因此需要写专门的函数进行操作。每个函数的操作均不一样,但也拥有相同的操作:

bool checkNode(Node *node, Types type) {
   if (node == NULL) {
      addLogNullNode(type);
      return false;
   }
   else if (node->type != type) {
      addLogTypeDismatch(type);
      return false;
   }
   addLogStartDealing(type);
   return true;
}

bool hasBrothers(Node *node, int n, ...) {
   va_list vaList;
   va_start(vaList, n);
   Types next;
   for (int i = 0; i < n; ++i) {
      next = va_arg(vaList, Types);
      if (node == NULL || next != node->type) return false;
      node = node->brother;
   }
   return node == NULL;
}

checkNode函数检查每个结点是否规范,hasBrothers函数判断某个节点是否只拥有若干个指定的兄弟。
在每个函数中,基本实现为:
(1)判断结点是否合法;
(2)检查孩子结点
(3)匹配文法并做出相应操作
(4)返回操作是否成功
例如:

bool dealWithProgram(Node *node) {
   if (checkNode(node, _Program) == false) return false;
   Node *c = node->child;
   if (c == NULL) {
      addLogNullChild(_Program);
      return false;
   }
   if (hasBrothers(c, 1, _ExtDefList)) {
      return dealWithExtDefList(c);
   }
   else {
      addLogWrongChild(_Program);
      return false;
   }
}

三、log.c文件
采用单独的日志文件进行输出。我们将需要输出的消息存储在一个消息链表里,在分析完文件后顺序输出消息。链表结构为:

#define MESSAGE_LENGTH 256

typedef struct Info {
   char *content;
   struct Info *next;
} Info;

typedef struct Log {
   Info *head;
   Info *tail;
} Log;

extern Log *SemanticError;
extern Log *SemanticAnalysisLog;

SemanticError存储语义错误消息,SemanticAnalysisLog存储分析日志,以供调试使用。
其中,日志包含的方法有:

Log *initLog();

bool addLogInfo(Log *log, char *content);

bool outputLog(Log *log, FILE *file);

bool reportError(Log *log, int type, int line, char *message);

在程序中,封装了以下方法,以供显示日志消息:

bool addLogNullNode(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "when dealing with %s, this %s is NULL.\n", typeToString(type), typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

bool addLogTypeDismatch(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "when dealing with %s, this node is not %s.\n", typeToString(type), typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

bool addLogNullChild(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "when dealing with %s, child node is NULL.\n", typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

bool addLogWrongChild(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "when dealing with %s, this %s has a wrong child.\n", typeToString(type), typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

bool addLogStartDealing(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "Start dealing with %s.\n", typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

bool addLogEmptyNode(Types type) {
   char message[MESSAGE_LENGTH];
   sprintf(message, "when dealing with %s, this %s is empty.\n", typeToString(type), typeToString(type));
   addLogInfo(SemanticAnalysisLog, message);
   return true;
}

四、main函数的改动
在main函数中,之前的操作不变,后面添加:如果词法分析和语法分析正确,则进行语义分析,并输出所有的信息:

int main(int argc, char **argv) {
   if (argc <= 1) {
      printf("pass filename to scanner\n");
      return -1;
   }
   else {
      FILE *f = fopen(argv[1], "r");
      if (!f) {
         printf("fopen failed:%s\n", argv[1]);
         return -1;
      }
      yyrestart(f);
      yyparse();
      FILE *semanticAnalysisLogFile = fopen("SemanticAnalysisLog.txt", "w");
      FILE *grammarTreeFile = fopen("grammarTree.txt", "w");
      FILE *hashTableFile = fopen("hashTable.txt", "w");
      if (syntax_correct && lexical_correct) {
         symbolTable = initializeHashSet(HASH_SIZE);
         SemanticAnalysisLog = initLog();
         SemanticError = initLog();
         printTree(root, 0, grammarTreeFile);
         dealWithProgram(root);
            checkFunction();
         outputLog(SemanticAnalysisLog, semanticAnalysisLogFile);
         outputHashTable(symbolTable, hashTableFile);
         outputLog(SemanticError, stdout);
//            printFunctions();
      }
      destroyTree(root);
      return 0;
   }
}

三、实验结果

  1. test1
int fun(){
	int i;
}

int main(){
	float f = 0.;
	f = f + 0.1;
	fun();
	return 0;
}

该例子无词法、语法、语义错误,故无输出

  1. test2
int main(){
        int i = 0;
        int i;                  // error 3
        j = i + 1;              // error 1
        inc(i);                 // error 2
}

int main(){                     // error 4
        return 3.5;             // error 8
}

成功检测出所有语义错误,其中第五行符合多个语义错误,将符合的每一个错误都输出出来了。

  1. test3
int main(){
        int i;
        float j = 9;            // error 5
        i = 3.7;                // error 5
        10 = i;                 // error 6
        i + 3.7;                // error 7
        return j;               // error 8
}

这里面的float j=9; 这句我们并没有报错,因为我们在词法分析中认为9可以作为一种浮点数。此外,我们使用gcc编译器实测,float j=9;并不会报错。
其他错误均与示例相同。

  1. test4
int func(){}
float func2(int m, float n){}

int main(){
        int i;
        float j[20];
        func();
        func(i);                // error 9
        func2(8, 8);            // error 9
        i[0];                   // error 10
        i();                    // error 11
        j[1.5] = 3.2;           // error 12
}

其中第12行实际上是不符合语法规范的,我们让语法检查对它“放行”,在语义检查中也能查到问题。

  1. test5
struct sa{
        int a;
        float b;
        int b;          // error 15
}c;
struct sf{
        int x;
};
struct sf{              // error 16
        int y;
};

int main(){
        struct sk mm;   // error 17
        struct sa nn;
        int a[10];
        c.f;            //error 14
        c.b;
        a.b;			//error 13
}

由于没有实现作用域的附加要求,这里面在第17行多报了一个错。

  1. testfj1
int f1();               // error 18
int f2();
int f2(int f);          // error 19
float f2();
int f3(){
	int x;
	{int x;}
}
int f4(){
	int x;
}

我们实现了对函数冲突声明和只声明不定义的检查,并能成功报错。由于未实现嵌套作用域的附加要求,第7行和第10行多报了两个错。

  1. testfj2
struct A{
    float a;
    int c;
};
struct B{
    float b;
    float c;
};
struct C{float m; int n;};
int main(){
    struct A aa;
    struct B bb;
    struct C cc;
    aa = bb;
    aa = cc;
}

同样地,由于作用域的问题,我们多报了一个错。
在结构体赋值时,由于aa和bb不等价,因此不能赋值,我们给出了报错。
而aa和cc是结构等价的,我们实现了把名等价变成结构等价的附加功能,因此aa和cc之间的赋值没有报错。

四、附完整代码

github 链接:https://github.com/zheliku/byyl---Semantic-Analysis。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1852200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

异地局域网纯软件组网如何设置?

在现代社会中&#xff0c;随着企业的不断扩张和分布&#xff0c;异地办公成为一种常见的工作模式。随之而来的是&#xff0c;如何实现异地局域网的组网设置成为了一个挑战。在这种情况下&#xff0c;采用纯软件组网方案是一种有效的解决方案。本文将介绍异地局域网纯软件组网设…

Redis数据库的删除和安装

Redis数据库的删除和安装 1、删除Redis数据库2、下载Redis数据库 1、删除Redis数据库 没有下载过的&#xff0c;可以直接跳到下面的安装过程↓ 我们电脑中如果有下载过Redis数据库&#xff0c;要更换版本的话&#xff0c;其实Redis数据库的删除是比较简单的&#xff0c;打开我…

字节大神强推千页PDF学习笔记,弱化学历问题,已拿意向书字节提前批移动端!

主要问java&#xff0c;以及虚拟机&#xff0c;问了一点android 1.实习项目有关的介绍以及问题回答 2.反射与代理的区别&#xff0c;动态代理&#xff0c;静态代理&#xff0c;二者的区别&#xff0c;以及代理模式的UML图 3.字节码技术 4.虚拟机的双亲委派&#xff0c;以及好…

1Panel应用推荐:Bitwarden开源密码管理器

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

贪心算法—

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法。这种算法并不总是能找到全局最优解&#xff0c;但在某些问题上能提供足够好的解决方案。贪心算法的关键特性包括&#…

判断题无答案22届期末复习

判断: 1-3.结构体变量不能进行整体输入输出。 1-4.不同类型的结构变量之间也可以直接赋值。 1-5假设结构指针p已定义并正确赋值,其指向的结构变量有一个成员是int型的num,则语句 (*p).num = 100; 等价于p->num=1…

1panel OpenResty 设置网站重定向

当我们部署网站时需要&#xff0c;输入"cheshi.com"域名回车&#xff0c;希望他自动跳转https://cheshi.com/indx/&#xff0c;而不是直接跳转https://cheshi.com时可以利用重定向来实现&#xff0c; 这里演示的是 1panel 如何设置&#xff08;nginx 貌似也是这样配…

【大疆pocket3】到手后5个必改初始设置关键点(下)

【大疆pocket3】到手后5个必改初始设置关键点&#xff08;下&#xff09; 一&#xff0c;简介二&#xff0c;必改关键点2.1 数字变焦2.2 慢动作拍摄2.3 神奇的小摇杆2.4 云台模式使用方法&#xff08;默认增稳模式和俯仰角锁定的差异化以及使用场景&#xff09;2.5 云台转向速度…

[HBM] HBM 国产进程, 国产HBM首次研发成功 (202406)

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解DDR》 AI 的火热浪潮带火了高带宽内存的需求&#xff0c;HBM已是存储市场耀眼的明星。目前市场上还没有国产HBM, 什么时候可以看到国产希望呢&#xff1f; 或许现在可以看到曙光了。 1. 设计端 1…

Linux查看公网IP的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

使用Android Studio导入源码

2-1 基础准备工作 首先你得安装配置了Android Studio&#xff0c;具体不明白的参考《Android Studio入门到精通 》。 接着你得下载好了源码Code&#xff0c;至于如何下载这里不再说明&#xff0c;比较简单&#xff0c;上官网查看就行了。 其次你需要保证源码已经被编译生成了…

压力测试

1.什么是压力测试 压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统在线上的处理能力和稳定性维持在一个标准范围内&#xff0c;做到心中有数 使用压力测试&#xff0c;我们有希望找到很多种用其他测试方法更难发现的错误&#…

写一个坏越的个人天地(一)

好久没写什么大点的项目了,今天想着写一个个人博客好了。I did it! 做个人天地。肯定得有个主题色吧。整个下拉界面,先准备三个色系吧 <el-header class="title"><el-dropdown @command="handleCommand"><span class="el-dropdown-…

DEV-C++与EasyX图形库

&#x1f3ae;&#x1f50a;本文代码适合编译环境&#xff1a;DEV-C&#x1f4bb; ✨&#x1f9e8;温馨提示&#xff1a;此文乃作者心血&#xff0c;如要转载请标注版权&#xff0c;否则视为抄袭&#xff01;&#x1f389;&#x1f3a0; 今天就算是我们Easyx教程的第一篇博文…

使用python下载图片且批量将图片插入word文档

最近有一个小的功能实现&#xff0c;从小某书上下载指定帖子的图片们&#xff0c;然后批量插入到word文档中&#xff0c;便于打印。于是有了以上需求。 一、下载图片 1、首先获取图片们的链接img_urls 首先&#xff0c;获取到的指定帖子的所有信息可以存入一个json文件中&am…

Python 深入学习局部函数和闭包函数

目录 局部函数与闭包函数的关联 变量捕获与状态保留 应用场景的交集与差异 闭包的本质 局部函数示例 闭包函数示例 局部函数和闭包函数之间存在着密切的联系&#xff0c;同时也有一些本质的区别。 局部函数与闭包函数的关联 局部函数&#xff08;Nested Function&#…

git 拉下来的项目,出现“加载失败”的解决方法

现象&#xff1a; 1、对加载失败的项目&#xff0c;尝试重新加载 解决思路&#xff1a;根据上面的提示&#xff0c;打开F盘对应的 .vcxproj文件&#xff0c;查看里面关于opencv454.props的内容 先删了&#xff0c;后面再补 2、当前的工作重点是消除加载失败的情况&#xff0c;…

VBA技术资料MF165:关闭当前打开的所有工作簿

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

RabbitMQ延时队列(实现定时任务)

消息的TTL(Time To Live)就是消息的存活时间。 RabbitMQ可以对队列和消息分别设置TTL。 对队列设置存活时间&#xff0c;就是队列没有消费者连着的保留时间。 对每一个单独的消息单独的设置存活时间。超过了这个时间&#xff0c;我们认为这个消息就死了&#xff0c;称之为死…

高速缓存存储器(Chche)

为了解决CPU和主存之间速度不匹配的问题&#xff0c;计算机系统中引入了高速缓存&#xff08;Chche&#xff09;的概念。 基本想法&#xff1a;使用速度更快但容量更小、价格更高的SRAM制作一个缓冲存储器&#xff0c;用来存放经常用到的信息&#xff1b;这样一来&#xff0c;…