解压后,除了本说明文档外,文件夹source
存放源代码,文件夹test
存放测试样例及exe运行程序。
注:务必保证exe文件和所有测试文件在同一文件夹下。
样例编号 | 样例信息 |
---|---|
1 | 书例14.1 鸡兔同笼问题 |
2 | 书例14.2 求最大公约数和最小公倍数 |
3 | 书例14.3 打印素数表 |
4 | 个人设计错误样例 |
5 | 书14.7.2 错误样例 |
6 | 个人设计正确样例(测试else和else if) |
7 | 个人设计正确样例(测试repeat...until) |
PL/0编译系统是一个编译-解释执行程序,整个编译过程分两个阶段进行。第一阶段先把PL/0源程序编译成假想计算机的目标(P-code指令)程序,第二阶段再对该目标程序进行解释执行,得到运行结果。
下面介绍一下该编译系统编写思路。
- <程序> ::= <分程序>.
- <分程序> ::= [<常量说明部分>][变量说明部分>][<过程说明部分>]<语句>
- <常量说明部分> ::= const<常量定义>{,<常量定义>};
- <常量定义> ::= <标识符>=<无符号整数>
- <无符号整数> ::= <数字>{<数字>}
- <标识符> ::= <字母>{<字母>|<数字>}
- <变量说明部分>::= var<标识符>{,<标识符>};
- <过程说明部分> ::= <过程首部><分程序>{;<过程说明部分>};
- <过程首部> ::= procedure<标识符>;
- <语句> ::= <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句- >|<复合语句>|<重复语句>|<空>
- <赋值语句> ::= <标识符>:=<表达式>
- <表达式> ::= [+|-]<项>{<加法运算符><项>}
- <项> ::= <因子>{<乘法运算符><因子>}
- <因子> ::= <标识符>|<无符号整数>|'('<表达式>')‘
- <加法运算符> ::= +|-
- <乘法运算符> ::= *|/
- <条件> ::= <表达式><关系运算符><表达式>|odd<表达式>
- <关系运算符> ::= =|<>|<|<=|>|>=
- <条件语句> ::= if<条件>then<语句>[else<语句>]
- <当型循环语句> ::= while<条件>do<语句>
- <过程调用语句> ::= call<标识符>
- <复合语句> ::= begin<语句>{;<语句>}end
- <重复语句> ::= repeat<语句>{;<语句>}until<条件>
- <读语句> ::= read'('<标识符>{,<标识符>}')‘
- <写语句> ::= write'('<标识符>{,<标识符>}')‘
- <字母> ::= a|b|...|X|Y|Z
- <数字> ::= 0|1|2|...|8|9
本编译系统采用 **C++**语 言编写。编译系统的编译器部分由词法分析、语法分析、语义分析及代码生成、符号表管理、错误处理五部分组成。整个编译器以语法分析程序为中心,调用词法分析程序识别单词,生成相应的代码,查询或填写相应的符号表项,在出错时调用错误处理程序,报告错误,同时跳过出错部分继续进行语法分析。
本编译系统有main.cpp
, words.cpp
, syntax.cpp
, tab.cpp
, error.cpp
, pcode.cpp
, interpret.cpp
七个类,下面是类中主要函数的介绍:
函数 | 作用 |
---|---|
getch | 取字符 |
getsym | 词法分析,读取一个单词 |
test | 测试当前单词符号是否合法 |
block | 分析程序处理 |
constdeclaration | 常量定义处理 |
vardeclaration | 变量定义处理 |
statement | 语句部分分析处理 |
expressison | 表达式分析处理 |
term | 项分析处理 |
factor | 因子分析处理 |
condition | 条件分析处理 |
error | 出错处理,打印出错代码和出错信息 |
enter | 登录符号表 |
position | 查找标识符在符号表中的位置 |
gen | 生成P-code指令,送入目标程序区 |
listcode | 列出P-code指令清单 |
base | 通过静态链接求出数据区的基地址 |
interpret | P-code解释执行程序 |
词法分析函数getsym
由语法分析程序调用,主要功能:
- 跳过源程序中的空格;
- 从源程序中识别出单词符号,并以枚举值的形式送入变量sym中;
- 用变量ID存放标识符,用二分查找保留字表,识别保留字;
- 如取来的单词为无符号整数,则将其转换为整数后存入变量num中。
实际的语法分析工作,在进入分程序(block
)以后可以分成两部分进行,首先是对说明部分的分析处理,这一部分主要调用函数constdeclaration
、vardeclaration
和block
;说明部分处理完后,对语句部分进行分析处理,即调用函数statement
,以及之后递归调用statement
、expression
、term
、factor
、condition
等。
在符号表管理中,符号表的录入是通过调用enter
函数实现的,符号表的查找是通过函数position
实现的。
可以将符号表理解成一个表格,其中储存着名字(name)、种类(kind)、值(val)、层次(level)、和地址(adr)。特别的,size是需要分配的数据区空间,仅procedure使用。
PL/0编译器在进行语法分析的同时通过gen
函数生成了P-code代码,即一个计算机的目标代码。最后用listcode
函数输出所有P-code.
指令含义见书P316.
Interpret
函数可模拟解释执行P-code代码,它采用栈式动态存储分配方法来分配程序运行时所需的数据存储空间。
错误处理中,PL/0编译器采用了test
函数,参数s1为合法符号集合,s2为停止符号集合,n为错误编码。test
函数主要负责两种功能:
- 在每个语法分析子程序的入口处,检测下一个取来的符号是否为该语法成分的合法后继符号或停止符号。若不是,则报告出错,并跳读一段源程序,直至取来的符号属于该语法成分的合法后继符号或停止符号集合为止;
- 测试当前读取的符号是否属于该语法成分的合法头符号集合。
下表为错误编码及出错信息:
错误编码 | 出错信息 |
---|---|
01 | 应是=而不是:= |
02 | =后应为常数 |
03 | 标识符后应为= |
04 | const, var, procedure后应为标识符 |
05 | 漏掉逗号或分号 |
06 | 过程说明后的符号不争取 |
07 | 应为语句 |
08 | 程序体内语句部分后的符号不正确 |
09 | 应为语句 |
10 | 语句之间漏分号 |
11 | 标识符未说明 |
12 | 不可向常量或过程赋值 |
13 | 应为赋值运算符:= |
14 | call后应为标识符 |
15 | 不可调用常量或变量 |
16 | 应为then |
17 | 应为分号或end |
18 | 应为do |
19 | 语句后的符号不正确 |
20 | 应为关系运算符 |
21 | 表达式内不可有 |
22 | 漏右括号 |
23 | 因子后不可为此符号 |
24 | 表达式不能以此符号开始 |
25 | 缺少分号 |
26 | 缺少until |
30 | 这个数太大 |
32 | 嵌套层数过多 |
40 | 应为左括号 |
感谢阅读这份说明文档。