一.扩展完成功能
- 语句注释;
- 扩展条件及短路计算;
- else语句实现;
- for语句实现;
- exit及break语句实现;
- 多维数组实现;
- 输入输出语句实现。
二.词法分析修改情况
- 原始PL0中的FIRST(S)集与FOLLOW(S)集情况如下表:
扩展之后做了以下修改:
1)语句(statement)的FIRST(S)添加以下元素:for,break,exit
2)条件(condition)的follow(S)添加了以下元素:not,or,and - 对于保留字、符号、指令的添加如下:
1)保留字:
对应的整形符号:
2)指令:
增加的各指令作用为:
JPNC:满足条件则跳转;
LDA :加载数组值到栈顶;
STA :存储数组值到偏移地址上;
RDA :读取数组指令;
WTA :打印数组指令;
READ:读取变量值;
WRITE:打印变量值。
oprcode在原有13条指令的基础上,额外添加了5条:
OPR_NOT:对栈顶值取非;
OPR_WEN:打印空格指令;
OPR_WRITE:打印栈顶常数指令;
OPR_ADDPLUS: 进行+=运算;
OPR_SUBPLUS: 进行-=运算。
3)符号:
‘&’,‘|’,‘!’其作用和and,or,not作用一样,可以用符号代替,而‘[’和‘]’用于数组的判断,‘%’用于求模运算。
三.实现过程
注释
在getsym( )函数中添加以下条件
当ch == ‘/’时如果,下一个字符是’*’,然后一直循环直到找到‘’*”,然后再取下一个字符‘/’。这样就跳过了/**/之间的所有字符,然后调用getsym( )取下一个符号
当连续出现两个//时,则令cc=ll,代表跳过该行的所有字符。然后继续getsym( )取下一个字符。条件扩展
添加了”or”,”and”,”not”关键字。
同时添加了以下全局变量:
下面看条件扩展代码:
通过重写ex_condition( )函数,调用condition( )函数。
注意: condition( )的follow(S)需要添加 sym_or,sym_and,sym_not;
上图描述的是只有一个条件的情况,即没有and,or 的计算。
注:如果第一个字符是not,则表示在生成条件指令之后,需要生成一个OPR_NOT指令,取反。
然后生成真、假值的调转指令,等待回填。
上面是分析了一个条件之后,遇到or的情况。遇到or之前,如果条件成立,则保持到真值链,生成JPNC指令,表示成立则调转。
否则继续分析,如果出现not,则分析完一个条件后,产生OPR_NOT进行求反。然后继续分析下一个条件。分析完下一个条件判断是否是then,如果是then则说明条件分析完了。
上面是分析完一个条件,遇到AND的情况。如果遇到AND条件之前,条件已经不成立,则说明该条件不成立,即保存到假值链,生成JPC指令等到回填。然后继续分析下一个条件。
分析完下一个条件判断是否是then,如果是then则说明条件分析完了。else语句实现
说明:else字句是在if语句中实现的:
将condition改成扩展后的ex_condition:
下图是真值出口回填:
如果条件成立,则跳到执行语句前面; 然后生成一个JMP指令,因为当如果存在else,那么必须跳过else的执行语句。因此这个JMP是有必要的,下面是判断是否存在else,以及条件生成假值链应该跳转的地方。
分析完‘;’后,再取一个字符,则看下一个字符是否是“else”,
如果是else,则假值链出口则是else执行语句的前面。
如果不是else,说明没有else子句,则假出口即是当前的cx。那么问题来了,statement的follow(S)是‘;’,因此if语句出去之后应该是一个“;”,而这时已经取了“;”后面的一个符号,这样将会倒是缺少“;”的错误。 为了解决该问题,另外定义两个变量:
又在getsym( )中以下修改:
如果sym_stack有预知符号,则下一个字符是预知符号。这样则能保证符号顺序读取。for语句实现,仿照c语法
语句的翻译方案如下:
语句的FIRST(S)修改如下:
代码如下:
真出口跳到循环体前面,假出口跳到循环体后面。循环体执行完后需要跳到更新语句;
而更新语句执行完后需要跳到条件判断语句。
上面最后涉及break语句,后面再说(有做修改)。break及exit语句的实现
break实现:
break实现代码如下(在for和while语句的后面加上):
这样break语句产生的调转指令将会跳到循环的后面。
exit实现:
直接生成OPR_RET指令即可。数组的实现
首先在pl.h头文件中,增加数组相关的参数,增加了四条和数组相关的指令,分别为:
LDA :加载数组值到栈顶;
STA :存储数组值到偏移地址上;
RDA :读取数组指令;
WTA :打印数组指令;
增加了数组结构:
修改getsym函数,处理’[’和’]’符号:
如果遇到左中括号,表示是数组类型,否则为标识符:
继续读取剩下的右中括号。
增加array_enter( )函数将ID_ARRAY类型填写入数组符号表中:
增加array_position()函数定位数组变量在数组符号表中的位置:
修改vardeclaration函数,增加对数组的处理:
在factor()函数中增加对数组的指令代码生成:
在term()函数和expression()函数的fsys集合中添加了数组相关的symtype值:
在statement()函数中增加对数组的处理,表示语句的左值为数组变量:
并生成STA指令存储数组到相应数组符号表中的位置。
在interpret()函数中增加数组四条指令的功能:
输入输出的实现
在pl0.h中添加了oprcode的类型OPR_WEN和OPR_WRITE,这两个指令的作用是通过生成指令opr,0,14打印回车符,生成指令opr,0,15打印输出数字或者const常量;添加opcode指令READ和WRITE,前者用于读入普通变量值,后者用于打印输出普通变量值;而opcode中的RDA和WTA指令分别用于读入数组变量值和打印输出数组变量值。
在statement函数中增加分句:
解释中间代码部分,在interpret函数添加如下:
+=、-=及%的实现
仿照其他运算符,并在fsys符号集合中添加+=和-=符号。
四.测试结果及分析
注释及条件扩展
pl0测试代码:
执行结果:
分析生成代码:
分析:上面的指令代码分析中详细说明了短路计算的过程是语句的跳转情况。if_else语句
Pl0测试代码:
结果:
for语句
pl0测试代码:
结果:
、break、exit语句实现
break:
结果:
当i+j=4时break跳出计算下一层循环。
exit:
结果:
数组声明、赋值及输入输出实现
Pl0测试代码:
结果:
读入数据数组变量1,2,3,4后:
加等、减等及求余运算的实现
Pl0测试代码:
结果:
五.思考
本次设计中较好地对短路计算进行了合理实现,没有额外增加判断大小的指令,而是调用了原condition()函数,利用原来的oprcode指令;输入输出语句的实现,不仅可以输入输出一般变量,还可以处理数组的输入输出,const常量的输出,数字的输出,并在read()和print()括号内赋予多参数,当print()的括号中不加参数为输出回车符。更详细的内容参见源代码。