语法分析之后,编译的任务是由已识别成功的正确源程序生成一组规格一致,便于计算加工的指令形式。
中间代码的生成方法:
语法制导翻译,属性文法制导翻译
中间代码:
翻译方法:语法制导翻译
语义子程序:
当我们进行了一次推导或者归约,我们就调用对应的语义子程序
也就是我们定义的变量或者运算的中间过程生成的值
图中的例子:当归约a+bc,当c入栈后,归约到bc为E,其语义栈的值为bc,再归约E+E,最后得到E,并语义栈为a+(bc),语义子程序就是保证,语义栈能跟随语法栈变化而变化,且语义子程序的过程需要符合我们语法中定义的运算规则
可以总结:语义是非终结符的。
常见中间代码形式:
布尔表达式的作用:
例子:
过程:首先对X+Y进行归约,然后读入>Z,生成四元式1;对T>Z进行归约,生成四元式2;又对(~BVC)归约,生成四元式3,4;进行^运算,生成四元式5;进行V运算生成四元式6。
可以通过短路的方式来直接得到值,而不需要逐个的运算。
例子:
只需要将程序指针指向一个个的四元式按照执行就可以得到程序的执行结果,首先判断A是否为真,若是就跳到5号四元式执行,否则再判断后面是否为真,若是跳到5号四元式执行,否则判断else是否成立,当我们执行完if判断进入的语句,需要在最后加上一个跳过else执行语句的四元式。
也就是将需要填写相同出口的四元式进行链表,串在一起。
拉链法:
这样的文法对于我们来说,可以原本的E->E1vE2,只能又一个语义表达式,这对于真加出口的回填是不利的,可以通过文法的改写成为E->E0E2;E0->E1v,这我们就得到了两个归约式,可以执行两个语义表达式,在不同的语义表达式中,将语义动作细分。
ETC和EFC存放的就是应该填写真或假的出口的四元式
例子:
在进行E->i的布尔量归约的时候,在E的TC栈(表示为真对应的出口)填入NXQ,在FC栈(表示为假对应的出口)
扫描到A,当A为真,那么下一个执行的四元式就是B
回填实现:
我们可以通过E0->E1v这个新增的归约式,来定义他的语义子程序,将下一个四元式的标号回填到E1的假出口上,然后将E1的真出口赋值给E0的真出口,再通过对其他的表达式的定义,完成E0的真出口回填。
解释:BACKPATCH就是将t填入到p中;当p不为0,取出p的第四项,给q,将t填入p的第四项,再将p的第四项赋值到q,循环直到q==0。这里可能还是有疑惑,可以向下看。
例子
上图为对关系运算的翻译,我们将Ea1 rop Ea2的真假出口指明
我们首先对A翻译生成两个真假出口,再翻译Av回填假出口,因为当A为真的时候,我们只需要跳到if语句的结果为真的地方执行,为假则需要进行B
MERG为合并操作
A归约为E1,E1v归约到E0,B
merg就是将p1连接到p2上;当p2为0表示p2对应的非终结符的真出口或假出口结点,就直接返回p1,否则就将p2写到p,当p的第四段内容不为0,就向后找,直达为0,说明到p2的头了,然后将p1连接到p2的尾部,返回。(感觉上面的ppt写错了)
这也是为什么backpatch操作有一个向后填写的操作
我认为的实现方法:
对于这个拉链法:当我们需要填写ETC和EFC时,可以先在E对于的TC和FC栈中创建一个链表头(可以在语义子程序中完成),链表结构可以为指针域,数据域(标号,四元式字符串),当我们需要去操作回填的时候,就可以将需要回填的标号按照链表填入字符串中
过程:识别A^ Bv ~ C:首先将A入栈为i(变量),i归约为E,E生成ETC和EFC,ETF填入真出口的四元式1,EFC填入假出口的四元式2;识别E^归约为EA,调用语义子程序,将下一个四元式地址填入ETC也就是1中,同时将E ^的假出口给EA;读入B,归约为E,通过语义子程序,将真假出口四元式入栈,为3和4;将EAE归约为E,调用语义子程序,调用语义子程序将TC给TC,进行串联(merg);这时候入栈v归约为E0,进行假出口回填;将~入栈,C入栈,这时候填入TC和FC栈; ~E归约为E,反转继承TC和FC;归约到E,FC继承,TC进行串联;通过这样的步骤,我们可以发现,A和B的假出口都跳到5,也就是判断C,A的真出口跳到判断B,B的真出口,表示A^B为真,为全局的真出口,C的假出口为全局的真出口,C的真出口为全局的假出口假出口
当扫描了then就需要回填E的真出口了
当扫描了else,需要生成一个S1的跳出(J,_ ,_ ,?),同时回填假出口
S*chain表示的就是整个if else语句的结束位置。
为了能对then和else进行语义子程序的编写
描述:首先读入if E then,进行归约,读到then了,可以回填E的真出口的,并且将E的假出口串联到C的chain栈中,当翻译当else,这时候,先生成一个跳过假出口语句四元式,出口不明朗,先可以回填E的假出口的了,也就是C的chain;然后S1的chain也就是s1中产生的需要跳过else的过程的句子;再归约T S2,S2也可能是if语句,也就是S2的链也是要跳过整个if……esle的,到了S,组成的S的链就是所有需要跳过if……else的链
if () xx
else if () xx
else xxx;
斜体部分就是可能的S2,我们完成了S2的翻译,S2的链条就是需要跳出S的,所以要传回到S的chain中,当S被归约时,就将跳出S的语句进行回填。
1,2为a的真假出口;3,4为b的真假出口;扫描到第一个then回填a的真出口,第二个then回填b的真出口,第一个else,添加跳过语句6并回填b的假出口,6语句连接到if b中;第二个else,添加跳过语句8并回填a的假出口,8语句连接到if a中,这时候,就可以进行C S1的归约,这时将C的chain(if a)和S1的chain(if b)连接在一起。
9和10为c的真假出口,当扫描到then,回填9号(真)四元式,扫描到else,添加跳过四元式12,并回填10号(假)四元式,12号四元式连接到if c中,到了执行C S2归约的时候了,将S2的链条(if c)连接到C的chain(if a)上