编译器笔记31-中间代码生成-控制流语句及其SDT
控制流语句的基本文法
控制流语句的基本文法.png注:S用于生成可执行语句的序列,一个可执行语句可以是赋值语句,也可以是一个分支语句(上图以if语句为例),也可以是循环语句(以while语句为例),可执行语句序列可构成程序P。
控制流语句的代码结构
例.png注:布尔表达式B被翻译成由跳转指令构成的跳转代码,指令的标号就例如以后提及的L1 L2等。
控制流语句的SDT
控制流语句的SDT.png对于第一个产生式P定义为S,首先S具有继承属性next。在S出现的语义动作中,我们要计算S.next,这里要调用newlabel函数(生成一个用于存放标号的新的临时变量L,返回变量地址。)将函数返回的变量地址赋值给S.next,此变量存放S执行完后下一条指令的标号。当S分析完毕以后也就是S的代码都生成完以后,那么我们就可以确定S之后下一条指令的标号,因此这时候我们调用label函数。lable函数将下一条三地址指令的标号赋值给L,在这里就是S.next。由此可见在S之前的语义动作只是确定了S的后置指令标号存放地址,但这个标号的具体值要等S分析完毕之后的语义动作添如。相当于在旅店先帮客人预订一个房间,等客人到达以后再入住,S.next相当于一个房间号,在S出现之前该房间号是可以确定的。
对于第二条产生式,在S1出现之前的语义动作中我们需要先计算S1的next属性。S1执行完以后是S2的第一条指令,当S1分析完毕以后下一条指令将是S2的第一条指令,此时调用label函数将下一条指令的标号赋值给S1.next,表示S1执行完以后将会执行S2的第一条指令。在S2执行之前的语义动作中我们需要计算S2的继承属性next,S2执行完以后的第一条指令就是S的执行指令也就是S.next指向的指令。因此S2.next=S.next。
第三条产生式是赋值语句。
问:为什么S1.next通过label(S1.next)赋值,而S2.next不用label(S2.next)赋值而用S2.next=S.next赋值。
答:因为在S1执行完之前的并不确定下一个指令地址因此需要等S1执行完后再调用label(S1.next)获取下个指令的地址。而在执行S1之前我们就知道下一个指令的地址为S.next因此在S1之前的语义动作就可以通过S2.next=S.next获得S2.next的值。