1.介绍
1.1游戏简介
1.2游戏的策略
1.3问题的描述
1.项目设计
2.1设计目标
这里设计了一个编译语言称作C-Minus(或简称为C-),这是一种适合编译器设计方案的语言,它比TINY语言更复杂,包括函数和数组。本质上它是C的一个子集,但省去了一些重要的部分,因此得名。首先,我们列出了语言惯用的词法,包括语言标记的描述。其次,给出了每个语言构造的BNF描述。同时还有相关语义的英语描述,包括语言标记的表述,再者给出了C-的几个示例程序。最后生成目标可执行代码(汇编代码)。
2.2项目环境
2.2.1开发与运行平台
Windows
2.2.2开发工具
VC6.0、Masm6.15以上版本
2.2.3项目设计模型
图1编译器的阶段
(1)扫描程序(scanner)
在这个阶段编译器实际阅读源程序(通常以字符流的形式表示)。扫描程序执行词法分析(Lexical analysis):它将字符序列收集到称作记号(token)的有意义单元中,记号同自然语言,如英语中的字词相似。因此可以认为扫描程序执行与拼写相似的任务。
例如在下面的代码行(它可以是C程序的一部分)中:
a [index] = 4 + 2
这个代码包括了12个非空字符,但只有8个记号:
a 标识符
[ 左括号
i n d e x 标识符
] 右括号
= 赋值
4 数字
+ 加号
2 数字
每一个记号均由一个或多个字符组成,在进一步处理之前它已被收集在一个单元中。
(2)语法分析程序(parser)
语法分析程序从扫描程序中获取记号形式的源代码,并完成定义程序结构的语法分析(syntax analysis),这与自然语言中句子的语法分析类似。语法分析定义了程序的结构元素及其关系。通常将语法分析的结果表示为分析树(parse tree)或语法树(syntax tree)。
例如,还是那行C代码,它表示一个称为表达式的结构元素,该表达式是一个由左边为下标表达式、右边为整型表达式的赋值表达式组成。这个结构可按下面的形式表示为一个分析树:
(1)语义分析程序(semantic analyzer)
程序的语义就是它的“意思”,它与语法或结构不同,程序的语义确定程序的运行,但是大多数的程序设计语言都在执行之前被确定而不易由语法表示和由分析程序分析的特征。这些特征被称作静态语义(staticsemantic),而语义分析程序的任务就是分析这样的语义(程序的“动态”语义具有只有在程序执行时才能确定的特性,由于编译器不能执行程序,所以它不能由编译器来确定)。一般的程序设计语言的典型静态语义包括声明和类型检查。由语义分析程序计算的额外信息被称为属性(attribute),它们通常是作为注释或“装饰”增加到树中(还可以将属性添加到符号表中)。
(2)代码生成器(codegenerator)
代码生成器得到中间代码(IR),并生成目标机器的代码,尽管大多数编译器直接生成目标代码,但是为了便于理解,本项目用汇编语言来编写目标代码。正是在编译的这个阶段,目标机器的特性成为了主要因素。当它存在与目标机器时,使用指令不仅是必须的而且数据的形式表示也起着重要的作用。
3.项目文件定义
3.1文件描述
C-编译器包括以下的文件,(为了包含而)把它的头文件放在左边,他的代码文件放在右边:
globals.h main.cpp
util.h util.cpp
scan.h scan.cpp
parse.h parse.cpp
symtab.h symtab.cpp
analyze.h analyse.cpp
code.h code.cpp
cgen.h cgen.cpp
这些文件的源代码都按顺序列在附录中。任何代码文件都包含globals.h头文件,它包括了数据类型的定义和整个编译器均使用的全程变量。main.cpp文件包括运行编译器的主程序,它还分配和初始化全程变量。其他的文件则包含了头/代码文件对、在头文件中给出了外部可用的函数原型以及在相关代码文件中的实现(包括静态局部函数)。scan、parse、analyze和cgen文件与图1中的扫描程序、分析程序、语义分析程序和代码生成器各阶段完全相符。util文件包括了实用程序函数,生成源代码(语法树)的内部表示和显示列表与出错信息均需要这些函数。symtab文件包括执行与C-应用相符的符号表的杂凑表。code文件包括用于依赖目标机器的代码生成的实用程序。图1还缺少一些其他部分:没有单独的错误处理器或
文字表且没有优化阶段;没有从语法树上分隔出来的中间代码;另外,符号表只与语义分析程序和代码生成器交互。
虽然这些文件中的交互少了,但是编译器仍有4遍:第1遍由构造语法树的扫描程序和分析程序组成;第2遍和第3遍执行语义分析,其中第2遍构造符号表而第3遍完成类型检查;最后一遍是代码生成器。在main.cpp中驱动这些遍的代码十分简单。当忽略了标记和编辑时,它的中心代码如下:
syntaxTree=parse();
buildSymtab(syntaxTree);
typeCheck(syntaxTree);
codeGen(syntaxTree,codefile);
为了灵活起见,我们还编写了条件编译标志,以使得有可能创建出一部分的编译器。如下是该标志及其效果:
标志 设置效果 编译所需文件(附加)
NO_COMMAND 创建不通过命令就可以运行的编译器
NO_PARSE 创建只扫描的编译器 globals.h,main.cpp,util.h,util.cpp, scan.h,scan.cpp,parse.h,parse.cpp
NO_ANALYZE 创建只分析和扫描的编译器 parse.h,parse.cpp
NO_CODE 创建执行语义分析, symtab.h,symtab.cpp,analyze.h,
但不生成代码的编译器 analyze.cpp
尽管这个C-编译器设计得有些不太实际,但却单个文件与阶段基本一致的好处,在VC6.0及以上版本的开发工具中可以编译运行。注意在编译运行期间可以适当修改源代码main.cpp文件,在编辑列表的信息中有若干选项,以下的标志均可用:
标志 设置效果
EchoSourse 将C-源程序回显到带有行号的列表
TraceScan 当扫描程序识别出记号时,就显示每个行号的信息
TraceParse 将语法树以线性