南京航空航天大学《编译原理》课程设计实验报告书

本文主要是介绍南京航空航天大学《编译原理》课程设计实验报告书,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:shmily

文章目录

    • C-语言的语法图描述
    • 系统设计
      • 系统亮点
        • 符号表的实现
        • 中间代码生成
      • 系统的总体结构
      • 主要功能模块的设计
      • 系统运行流程
    • 系统实现
      • 系统主要函数说明(主要功能、输入\输出、实现思想)
        • cminus.l
        • cminus.y
        • buildSymtab
        • st_insert()、st_create()
        • st_lookup()、st_lookup_top()
        • typeCheck()
        • codeGen()
        • insertQuad()
        • cGen()
    • 课程设计心得
    • 源码

C-语言的语法图描述

在这里插入图片描述

系统设计

系统亮点

符号表的实现

符号表采用了哈希表的形式,可以方便地查找、插入和删除,但是问题也随之而来,就是符号的作用于较难跟踪。很有可能同一名称的变量在不同作用于出现,如果孤立地对待每一个变量便会出现变量冲突。因此我们的符号表将作用域做了栈式处理,例如下图:

在这里插入图片描述

再将栈中每一个元素,即每一个作用域做延伸,对每一个作用域建立一个哈希表,将范围缩小。这样一来可以对每一个变量框在一个作用域中进行跟踪。总体数据结构如下:

在这里插入图片描述

中间代码生成

我们的中间代码生成运用一遍扫描的方法自下而上归约,但是和课本的方法有出入,进行了一些改变。首先,建立一个链表存储所有的生成的四元式,每个四元式的本质是一个结构体。其中,在对if-else或是while这种需要回填的部分处理时,先对判断条件进行判断,结果存在一个临时变量t之中,这个变量的值非0即1.

当面临if语句规约时,判断的是t的值,真出口就是继续执行的下一条语句,执行完语句之后,我们生成了又一个临时变量L,并且新增一个操作叫做label,它用来指示整个if-else语句的结尾,这个label四元式是在控制语句的结尾,当生成(label,L2,-,-)的同时,顺序查找四元式链表中j_if_false后紧跟的(j,-,-,-),并将其填为(j,L2,-,-),代表无条件跳转到L2。

对于假出口的处理同理,只不过是在else语句开始之前就生成一个(label,L1,-,-),记下标号,并且在生成之后顺序查找四元式链表,找到j_if_false开头的代码进行回填。这样,真假出口的回填就完成了。

其他的四元式根据不同节点的类型直接生成不同的四元式,最后将四元式链表输出到文件当中即可。
在这里插入图片描述

系统的总体结构

结构框图如图所示,共分为五大部分以及两个辅佐部分,五大部分为词法分析、语法分析、建立符号表、类型检查和中间代码,辅佐部分为符号表和语法树。

在这里插入图片描述

主要功能模块的设计

**词法分析和语法分析:**利用flex & bison工具实现,在cminus.l文件中写好词法规则和getToken函数,在cminus.y文件中写好BNF语法和其他相关函数即可,flex和bison可以帮助我们生成lex.yy.c文件和.tab.文件。

**语义分析:**生成语法分析树和符号表,借助这两个数据结构分析语义。

**中间代码生成:**通过一遍扫描和回填技术实现中间代码生成。

系统运行流程

C-minus编译器运行于Linux系统,在/compiler文件下执行sh脚本即可编译运行整个项目,例如测试gcd.c文件:

./compiler.sh ../testcase/gcd.c

之后在/testcase文件中可以查看该测试样例对应生成的中间代码,记录在一个.txt文件中。

在这里插入图片描述

系统共分为三个部分:词法分析器、语法分析器、语义分析与中间代码产生器。

首先,编译器会对输入的文件进行词法分析,分割成一个个的单词符号,其次,进行语法分析,识别出各类语法单位,判断输入串是否构成语法上正确的“程序”,最后按照语义规则对语法分析器归约出的语法单位进行语义分析并把他们翻译成一定形式的中间代码。

系统实现

系统主要函数说明(主要功能、输入\输出、实现思想)

cminus.l

这一文件的主要功能为flex分析器的文件。第一部分为定义,flex工具在处理这一部分的时候,会将第一部分的所有代码原封不动的插入到 lex.yy.c(词法分析代码)中,我们在这一部分中插入了程序需要用到的头文件等内容。第二部分是词法规则部分:正则表达式+代码片段。当匹配相对应的正则表达式时,这些代码片段就会被执行。我们的代码片段为一个单词符号的编码。最后一个部分包括辅助函数,它们用于在第2个部分被调用且不在任何地方被定义的辅助程序。

cminus.y

这一文件的主要功能是产生语法分析代码。首先是定义部分:定义部分包括bison需要用来建立分析程序的有关记号、数据类型以及文法规则的信息,还包括了一些头文件。规则部分:包括修改的BNF格式中的文法规则以及将在识别出相关的文法规则时被执行的C代码中的动作。我们根据每一条产生式执行不同的语义动作,例如生成节点、赋上相应属性等等。最后是用户自定义函数部分,我们定义了几个插入节点的函数,可以和语义动作对应起来。最后这个文件会被建立成两个.tab.程序,实现语法分析和语法树的建立。

具有一定的检错能力:例如对这样一行代码

rem = x-(div*10;

缺少匹配的右括号,在编译器中便停在了这一行的;处,并报语法错误。

在这里插入图片描述

buildSymtab

Cminus的语义分析器在analyze.csymtab.c中,其对应的主要功能,analyse.c是用于语义分析本身,而symtab.c则是用于生成其对应的符号表。

进入语义分析部分的代码在main.c中:

#if !NO_ANALYZEif (! Error) {if (TraceAnalyze) fprintf(listing, "\nBuilding the symbol table...\n");buildSymtab(syntaxTree);if (TraceAnalyze) fprintf(listing, "\nChecking type...\n");typeCheck(syntaxTree);if (TraceAnalyze) fprintf(listing, "\nType check completed!\n");}

在语义分析之前,编译器最开始的输入是一段代码,经过词法分析,输出的是词法单元,从而被语法分析单元所识别;语法分析的输入是一个个词法单元,通过分析这些词法单元之间的逻辑,利用递归下降等方法,形成一棵语法树,并将语法树的根结点存储在一个TreeNode类中,从而通过根结点就可以实现对于整个语法树的遍历(一般是前序遍历);之后,来到了语义分析部分,语义分析的输入是一个语法树,这里我们的输入是根结点;语义分析的输出,则是符号表和语法报错信息。

符号表的意义在于,分析代码中所有的声明,比如变量函数等内容;而语法报错信息,则会通过语法树结点关系,检测相邻词法单元是否符合文法规则:比如,int 1和int a两种输入,在语法分析阶段均可通过,但是在语义分析阶段,int 1会被识别为一个错误,因为根据语法规则,int是一个声明,声明后面只能跟着一个变量名ID,而词法单元1的属性是NUM,int后面是不允许接着一个NUM的。

函数buildSymtab构造符号,通过语法树的遍历构建。

/* Function buildSymtab constructs the symbol* table by preorder traversal of the syntax tree*/
void buildSymtab(TreeNode * syntaxTree) {globalScope = sc_create((char *) "ESCOPO_GLOBAL");sc_push(globalScope);traverse(syntaxTree, insertNode, afterInsertNode);sc_pop();sys_free();if(mainDeclaration == FALSE) {fprintf(listing, "Declaration Error: Undeclared main function\n");return;}if (TraceAnalyze) {fprintf(listing,"\nSymbol Table:\n");printSymTab(listing);}
}

该函数共分为四个步骤,首先调用sc_create()函数创建一个作用域栈,作用域栈用于储存一个符号的作用域,如果给出作用域名称作为参数,它将分配内存并填写每个属性。在初始化的时候,如果是全局变量,则它就是最大的全局作用域,如果不是,则把指针设置为该作用域所属的较大作用域的父作用域。
在这里插入图片描述

例如,名为main的作用域将global作为其父作用域。 在main中创建另一个作用域时,它以相同的方式堆叠在栈上。 当main结束时(遇到复合语句时),main作用域弹出并查看全局作用域中的其余TreeNodes。

Scope sc_create(char * funcName) {Scope newScope = (Scope) malloc(sizeof

这篇关于南京航空航天大学《编译原理》课程设计实验报告书的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821562

相关文章

idea maven编译报错Java heap space的解决方法

《ideamaven编译报错Javaheapspace的解决方法》这篇文章主要为大家详细介绍了ideamaven编译报错Javaheapspace的相关解决方法,文中的示例代码讲解详细,感兴趣的... 目录1.增加 Maven 编译的堆内存2. 增加 IntelliJ IDEA 的堆内存3. 优化 Mave

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

kotlin中的模块化结构组件及工作原理

《kotlin中的模块化结构组件及工作原理》本文介绍了Kotlin中模块化结构组件,包括ViewModel、LiveData、Room和Navigation的工作原理和基础使用,本文通过实例代码给大家... 目录ViewModel 工作原理LiveData 工作原理Room 工作原理Navigation 工

Java的volatile和sychronized底层实现原理解析

《Java的volatile和sychronized底层实现原理解析》文章详细介绍了Java中的synchronized和volatile关键字的底层实现原理,包括字节码层面、JVM层面的实现细节,以... 目录1. 概览2. Synchronized2.1 字节码层面2.2 JVM层面2.2.1 ente

MySQL的隐式锁(Implicit Lock)原理实现

《MySQL的隐式锁(ImplicitLock)原理实现》MySQL的InnoDB存储引擎中隐式锁是一种自动管理的锁,用于保证事务在行级别操作时的数据一致性和安全性,本文主要介绍了MySQL的隐式锁... 目录1. 背景:什么是隐式锁?2. 隐式锁的工作原理3. 隐式锁的类型4. 隐式锁的实现与源代码分析4

MySQL中Next-Key Lock底层原理实现

《MySQL中Next-KeyLock底层原理实现》Next-KeyLock是MySQLInnoDB存储引擎中的一种锁机制,结合记录锁和间隙锁,用于高效并发控制并避免幻读,本文主要介绍了MySQL中... 目录一、Next-Key Lock 的定义与作用二、底层原理三、源代码解析四、总结Next-Key L

Spring Cloud Hystrix原理与注意事项小结

《SpringCloudHystrix原理与注意事项小结》本文介绍了Hystrix的基本概念、工作原理以及其在实际开发中的应用方式,通过对Hystrix的深入学习,开发者可以在分布式系统中实现精细... 目录一、Spring Cloud Hystrix概述和设计目标(一)Spring Cloud Hystr