# compiler **Repository Path**: wasp-11111/compiler ## Basic Information - **Project Name**: compiler - **Description**: 编译器,基于一个较为原始的llvm进行设计而不是使用官方llvm - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-09-06 - **Last Updated**: 2023-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 总述 我们完成了**词法分析,LL1语法分析,LR1语法分析和中间代码生成** **提交的代码分为两个项目**,一个是包含**词法分析与LL1分析**,另一个是包含**词法分析、LR1分析和中间代码生成(存放路径不能有中文)** 关于中间代码生成,由于给的部分样例内容不对,所以我们在实现时既参考了助教给的样例,也参考了llvm文档和用官方软件生成的中间代码 官方文档: https://llvm.org/docs/LangRef.html#store-instruction 官方软件地址: https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0 在上述release页面选择对应的exe执行文件安装,如果是windows,则选择如下安装包(其他平台则选择对应的执行文件): ![image-20230525192835181](assets/image-20230525192835181.png) 在安装的过程中,选择为当前用户添加环境变量,确保等下的命令可以被终端识别: ``` clang -cc1 test.cpp -emit-llvm ``` 使用上述命令可以用c++文件(如test.cpp,但是**这样的cpp文件必须符合c/c++语法**)生成用于参考的中间代码,会在相同目录下生成test.ll作为参考 我们在使用助教给的库实现中间代码时**同时考虑了给的样例和官方的中间代码**,确保有正确的结果 然后我们针对助教给的llvm库,前期先做了代码分析,写了分析文档附在提交文件里: ![image-20230525194629391](assets/image-20230525194629391.png) **下面分别介绍两个项目的运行:** - 词法分析与LL1分析 - 词法分析+LR1语法分析+中间代码生成 ## 词法分析与LL1分析 #### 运行环境为visual studio 2022 如果使用2019版本**会报错**(因为sdk不一样),用**2022版本**打开就可以**一键运行**(是基于2022版本开发的) ![image-20230525171611322](assets/image-20230525171611322.png) 在main.cpp中: ```c++ string inputs[6] = { "tests/00/00.txt", "tests/01/01.txt", "tests/02/02.txt", "tests/07/07.txt", "tests/08_编译错误示例/08.txt", "tests/10_编译错误示例/10.txt" }; string outputs_lexical[6] = { "tests/00/00_my_lexical.txt", "tests/01/01_my_lexical.txt", "tests/02/02_my_lexical.txt", "tests/07/07_my_lexical.txt", "tests/08_编译错误示例/08_my_lexical.txt", "tests/10_编译错误示例/10_my_lexical.txt" }; string outputs_grammar[6] = { "tests/00/00_my_grammar.txt", "tests/01/01_my_grammar.txt", "tests/02/02_my_grammar.txt", "tests/07/07_my_grammar.txt", "tests/08_编译错误示例/08_my_grammar.txt", "tests/10_编译错误示例/10_my_grammar.txt" }; ``` 确定了输入的测试文件和输出文件路径,只需修改这里就可以测试不同文件 输出文件放在tests子目录对应文件夹下: ![image-20230525173005960](assets/image-20230525173005960.png) ![image-20230525173136970](assets/image-20230525173136970.png) 我们的输出和示例是完全相同的,我们还写了python脚本用于对比,结果在输出网页里 代码文件结构如下: ``` main.cpp --- 程序入口 grammer.cpp grammer.h --- 语法分析基础代码相关 LL1.cpp LL1.h --- LL1实现相关 dfa.cpp nfa.cpp nfa.h --- 词法分析相关 tool.cpp --- 一些工具类 ``` 输出如下(是完全一致的): ![image-20230525204115393](assets/image-20230525204115393.png) ## 词法分析+LR1语法分析+中间代码生成 ### 使用方法 ``` cmake ``` 因为原仓库使用`cmake`进行编译运行,我们也以`cmake`形式编译运行 我们使用了**clion软件**来运行这个cmake项目,运行只需用**clion**打开这个项目,一键运行即可,cmake已经配置好了。 或者使用其他cmake编译软件,但要注意,请把文法文件grammer.txt放到生成的可执行文件同级目录下,如下: ![image-20230525181828125](assets/image-20230525181828125.png) **注意:**如果代码可运行但是无法识别,很大概率是grammar.txt放错位置 **特别注意:路径中不能有中文** 直接点击运行会重复六次编译前端的前过程,输入文件如下,若想要更改输入则在下述地址进行你想要的修改: ```c++ string inputs[6] = { "../tests/00/00.txt", "../tests/01/01.txt", "../tests/02/02.txt", "../tests/07/07.txt", "../tests/08_error/08.txt", "../tests/10_error/10.txt" }; ``` 以/tests/00这个目录为例,我们词法分析的输出保存在`00_my_lexical.txt`中,LR1语法分析的输出保存在`00_my_grammar.txt`,中间代码生成输出保存在`00_my_IR.ll`中,以次类推。 !**注意,若六个输入文件中某个文件出现语法错误或语义错误,将会引发程序的异常退出,并不是程序本身报错,是我们写的简单的错误处理,并且那个文件之后的文件都不会进行编译。请老师助教测试时注意输入语句的语法和语义正确,测试是否能识别错误时尽量放在tests最后一个文件夹中!** ### 关于文件结构的说明 `main.cpp` 是程序的入口 `src`是包含源码的目录,在这个目录下包括了代码的的所有`cpp`文件,类函数的实现都在这个文件夹下 `include`是包含头文件的目录,在这个目录下包括了代码所有的`h`文件,类的声明全都在这个文件夹下 `cmake-build-debug`是使用`cmake`进行编译后存储执行文件和debug信息的位置 `tests`是测试用例和输出文件保存的地方 `CmakeLists.txt`是编译的配置文件 ### 代码运行结果说明 **注意:LR1实现时使用了#替代EOF,因为单个字符方便识别,提高运行效率** 输出结果在tests文件夹下: ![image-20230525200942929](assets/image-20230525200942929.png) 中间代码结果展示如下: 左边我们的结果,左边是给的样例 ![image-20230525201420569](assets/image-20230525201420569.png) ![image-20230525201626849](assets/image-20230525201626849-1685016987966-1.png) 对于样例2,和助教讨论后,发现有明显的错误,如: ``` store i32 %op3, i32 1 ``` 这一句,顺序颠倒,还有应该使用i32*而不是i32(参考助教给的官方文档),还有多生成了较多实际不会产生的中间代码,所以我们参考了官方软件的生成: ![image-20230525202108791](assets/image-20230525202108791.png) 由于我们的文法和c并不完全一致(我们有void函数返回值),是使用的下面代码来使用官方llvm进行中间代码生成: ![image-20230525202233758](assets/image-20230525202233758.png) 所以官方会比我们多一个变量`%retval`,其他的我们和官方一致 **对于样例7**,代码如下(使用官方生成时void变为int): ``` void main(){ const int a = 10, b = 5; return b; } ``` ![image-20230525202823345](assets/image-20230525202823345.png) 我们的官方几乎一样(多出的%retval是因为返回值设置为int来满足c语法了),示例输出节省了很多步骤(理论上该有),所以我们这里更多的是参考官方生成。 由于错误样例不会有语法分析,所以也没有中间代码生成