编译原理教程课后习题答案——
第四章第四章 语义分析和中间代码生成语义分析和中间代码生成 4.1完成下列选择题: (1) 四元式之间的联系是通过实现的。 a. 指示器 b. 临时变量 c. 符号表 d. 程序变量 (2) 间接三元式表示法的优点为。 a. 采用间接码表,便于优化处理 b. 节省存储空间,不便于表的修改 c. 便于优化处理,节省存储空间 d. 节省存储空间,不便于优化处理 (3) 表达式(┐A∨B)∧(C∨D)的逆波兰表示为。 a. ┐AB∨∧CD∨ b. A┐B∨CD∨∧ c. AB∨┐CD∨∧ d. A┐B∨∧CD∨ (4) 有一语法制导翻译如下所示: S→bAb {print″1″} A→(B {print″2″} A→a {print″3″} B→Aa) {print″4″} 若输入序列为 b(((aa)a)a)b,且采用自下而上的分析方法,则输出序列为。 a. 32224441 b. 34242421 c. 12424243 d. 34442212 【解答】 (1) b (2) a (3) b (4) b 4.2何谓“语法制导翻译”?试给出用语法制导翻译生成中间代码的要点, 并用 一简例予以说明。 【解答】语法制导翻译(SDTS)直观上说就是为每个产生式配上一个翻译子程序 (称语义动作或语义子程序), 并且在语法分析的同时执行这些子程序。 也即在语法分析过程 中, 当一个产生式获得匹配(对于自上而下分析)或用于归约(对于自下而上分析)时, 此产生 式相应的语义子程序进入工作,完成既定的翻译任务。 用语法制导翻译(SDTS)生成中间代码的要点如下: (1) 按语法成分的实际处理顺序生成,即按语义要求生成中间代码。 (2) 注意地址返填问题。 (3) 不要遗漏必要的处理,如无条件跳转等。 例如下面的程序段: if (i0) a=i+e-b*d; else a=0; 在生成中间代码时,条件“i0”为假的转移地址无法确定,而要等到处理“else”时 方可确定,这时就存在一个地址返填问题。此外, 按语义要求,当处理完(i0)后的语句(即 “i0”为真时执行的语句)时,则应转出当前的 if 语句,也即此时应加入一条无条件跳转 指令,并且这个转移地址也需要待处理完else 之后的语句后方可获得,就是说同样存在着 地址返填问题。 对于赋值语句 a=i+e-b*d, 其处理顺序(也即生成中间代码顺序)是先生成 i+e 的代码,再生成 b*d 的中间代码,最后才产生“-”运算的中间代码,这种顺序不能颠倒。 4.3令 S.val 为文法 G[S]生成的二进制数的值,例如对输入串101.101,则S.val=5.625。 按照语法制导翻译方法的思想,给出计算S.val 的相应的语义规则,G(S)如下: G[S]: S→L.L|L . L→LB|B B→0|1 【解答】计算 S.val 的文法 G′[S]及语义动作如下: 产生式语义动作 G′[S]:S′→S {print(S.val)} S→L1·L2 {S.val:=L1.val + L2.val/2L2.length} S→L { S.val:=L.val } L→L1B {L.val:=L1.val*2 + B.val L.length:=L1.length +1} L→B{L.val:=B.val L.length:=2} B→1{B.val:=1} B→0 {B.val:=0} 4.4下面的文法生成变量的类型说明: D→id L L→,id L|:T T→integer|real 试构造一个翻译方案,仅使用综合属性,把每个标识符的类型填入符号表中(对所 用到的过程,仅说明功能即可,不必具体写出)。 【解答】 此题只需要对说明语句进行语义分析而不需要产生代码, 但要求把每个标识符的 类型填入符号表中。对 D、L、T,为其设置综合属性 type,而过程 enter(name,type)用来 把名字 name 填入到符号表中,并且给出此名字的类型type。翻译方案如下: D→id L {enter (id.name, L.type);} L→,id L(1) {enter (id.name, L(1).type); L.type=L(1).type;} L→:T {L.type=T.type;} T→integer {T.type=integer;} T→real {T.type=real;} 4.5写出翻译过程调用语句的语义子程序。在所生成的四元式序列中,要求在转 子指令之前的参数四元式par 按反序出现(与实现参数的顺序相反)。 此时, 在翻译过程调用 语句时,是否需要语义变量(队列)queue? 【解答】 为使过程调用语句的语义子程序产生的参数四元式par 按反序方式出现, 过程调 用语句的文法为 S→call i(arglist) arglist→E arglist→arglist(1),E . 按照该文法,语法制导翻译程序不需要语义变量队列queue,但需要一个语义变量 栈 STACK,用来实现按反序记录每个实在参数的地址。翻译过程调用语句的产生式及语义子 程序如下: (1) arglist→E { 建立一个 arglist.STACK 栈,它仅包含一项 E.place} (2) arglist→arglist(1), E { 将E.place压入arglist(1). STACK栈, arglist. STACK=arglist(1).STACK} (3) S→call i (arglist) {while arglist.STACK≠null do begin 将 arglist.STACK 栈顶项弹出并送入 p 单元之中; emit (par,_ ,_ ,p); end; emit (call,_ ,_ , entry (i));} 4.6设某语言的 while 语句的语法形式为 S→ while E do S(1) 其语义解释如图 4-1 所示。 (1) 写出适合语法制导翻译的产生式; (2) 写出每个产生式对应的语义动作。 E的 代 码 假 真 S(1)的 代 码 图 4-1习题 4.6 的语句结构图 【解答】本题的语义解释图已经给出了翻译后的中间代码结构。在语法制导翻译过程中, 当扫描到 while 时,应记住 E 的代码地址;当扫描到 do 时,应对 E 的“真出口”进行回填, 使之转到 S(1)代码的入口处;当扫描到 S(1)时,除了应将 E 的入口地址传给 S(1).chain 之外,还要形成一个转向E 入口处的无条件转移的四元式, 并且将 E.fc 继续传下去。因此, 应把 S→while E do S(1) 改写为如下的三个产生式: W→while A→W E do S→A S(1) 每个产生式对应的语义子程序如下: W→while { W.quad=nxq;} A→W E do { Backpatch(E.tc,nxq); A.chain=E.fc; A.quad=W.quad;} S→A S(1) { Bac