第六章语法制导译 在前面已经介绍了编译程序构造的二个重要阶段,即词 法分析和语法分析。现在再来介绍编译程序的另一个重要阶 段 中间代码生成。虽然在实际应用中,是否采用中间代 码形式是根据实际情况而定的。但事实上,为了使编译程序 的结构清晰、简单、明确,多数编译程序采用了中间代码的 形式。尤其是使用了中间代码的形式,使目标代码优化比较 容易实现。通常以中间代码生成这一阶段来划分编译程序的 前端和后端。对于不同的高级语言只要翻译成相同的中间代 码,再接上一个相同的把中间代码翻译成目标代码的后端」 就可以形成不同的编译程序。同一种高级语言只要翻译成相 同的中间代码就可以共用一个前端,接上后端可以在不同机 型上实现同一语言的编译程序。虽然中间代码的形式很多, 但常见的中间代码有逆波兰式、三元式、四元式、树形表示 等。本章讨论如何将高级语言翻译成中间代码
第六章 语法制导译 在前面已经介绍了编译程序构造的二个重要阶段,即词 法分析和语法分析。现在再来介绍编译程序的另一个重要阶 段——中间代码生成。虽然在实际应用中,是否采用中间代 码形式是根据实际情况而定的。但事实上,为了使编译程序 的结构清晰、简单、明确,多数编译程序采用了中间代码的 形式。尤其是使用了中间代码的形式,使目标代码优化比较 容易实现。通常以中间代码生成这一阶段来划分编译程序的 前端和后端。对于不同的高级语言只要翻译成相同的中间代 码,再接上一个相同的把中间代码翻译成目标代码的后端, 就可以形成不同的编译程序。同一种高级语言只要翻译成相 同的中间代码就可以共用一个前端,接上后端可以在不同机 型上实现同一语言的编译程序。虽然中间代码的形式很多, 但常见的中间代码有逆波兰式、三元式、四元式、树形表示 等。本章讨论如何将高级语言翻译成中间代码
61中间代码的形式 中间代码的形式虽然很多,但组成中间代码的原则是: (1)形式比较简单,容易翻译成相应的目标机器代码 (2)能充分反映源程序的特点 比较常用有逆波兰式、三元式、四元式和树型表示。 6.11逆波兰式 逆波兰式是由波兰数学家卢卡西维奇发明的一种表示算 术表达式或逻辑表达式的方法,它是一种能表示运算符的计 算顺序,但没有括号的表达式。在这种表示法中,把运算符 直接跟在运算对象后面,因此逆波兰表示法又称后缀表示法
6.1 中间代码的形式 中间代码的形式虽然很多,但组成中间代码的原则是: (1) 形式比较简单,容易翻译成相应的目标机器代码 (2) 能充分反映源程序的特点 比较常用有逆波兰式、三元式、四元式和树型表示。 6.1.1 逆波兰式 逆波兰式是由波兰数学家卢卡西维奇发明的一种表示算 术表达式或逻辑表达式的方法,它是一种能表示运算符的计 算顺序,但没有括号的表达式。在这种表示法中,把运算符 直接跟在运算对象后面,因此逆波兰表示法又称后缀表示法
逆波兰表示法的格式为: <运算对象1><运算对象2<运算符> 例如:表达式(a+b*c/()e-f的逆波兰式为 abcd/ te*f 从上可以看出,逆波兰式具有下列性质: 1)在中缀式和逆波兰式中,运算对象是按相同的顺序出现 2)在逆波兰式中,运算符是按计算顺序(从左到右)出现 3)运算符紧跟在其运算对象后面出现的
逆波兰表示法的格式为: <运算对象1> <运算对象2><运算符> 例如:表达式(a+b*c/d)*e-f的逆波兰式为: abc*d/+e*f- 从上可以看出,逆波兰式具有下列性质: (1) 在中缀式和逆波兰式中,运算对象是按相同的顺序出现 的 (2) 在逆波兰式中,运算符是按计算顺序(从左到右)出现 的 (3) 运算符紧跟在其运算对象后面出现的
当逆波兰式允许单目运算符(仅允许一个运算对象的运 算符)出现时,会产生一些问题。如写出表达式a+(-bcd)*e 的逆波兰式为ab-cd/e*,此时很难区分运算符-是单目的还是 双目的。即先计算a-b还是-b。解决上述问题的方法有二种: (1)把单目运算符改成双目,如改写成:a0b-cd/e* (2)引进新的运算符为@,如:ab@cd/"e* 其它单元目的运算符可参照上述方法处理。对于第一种方法 把单目运算符处理成双目运算符增加了运算的时间,降低 工作效率。对于第二种方法,要解决的问题是如何把符号 处理成不同的符号。处理的时机可以放在词法分析中解决, 如在表达式的起始位(如赋值号后、逗号后、左括号后等) 设置标记19为1,即单元且运算符,在遇到运算对象后设置 标记fag为0,即双目运算符
当逆波兰式允许单目运算符(仅允许一个运算对象的运 算符)出现时,会产生一些问题。如写出表达式a+(-b*c/d)*e 的逆波兰式为ab-cd/*e*,此时很难区分运算符-是单目的还是 双目的。即先计算a-b还是-b。解决上述问题的方法有二种: (1) 把单目运算符改成双目,如改写成:a0b-cd/*e* (2) 引进新的运算符为@,如:ab@cd/*e* 其它单元目的运算符可参照上述方法处理。对于第一种方法, 把单目运算符处理成双目运算符增加了运算的时间,降低了 工作效率。对于第二种方法,要解决的问题是如何把符号‘- ’处理成不同的符号。处理的时机可以放在词法分析中解决, 如在表达式的起始位(如赋值号后、逗号后、左括号后等) 设置标记flag为1,即单元目运算符,在遇到运算对象后设置 标记flag为0,即双目运算符
计算逆波兰式表示的算术或逻辑表达式比计算中缀式要 简单。这是因为计算逆波兰式不要比较运算符的优先级,只 要一遇到运算符就立即可以计算。具体实现中只要用一个栈 来存放运算对象,故该栈又称运算对象栈或运算分量栈。在 栈中存放未被计算的运算对象 旦扫描到运算符时,就 从栈中取出运算符所需的运算对象个数进行计算。然后再将 计算结果放入栈中,当全部扫描完逆波兰式后栈顶元素即为 最后的计算结果。 般算法语言除了算术表达式和逻辑表达式外,还有其 它如赋值语句、条件语句、循环语句等,但只要遵守运算对 象后直接紧跟计算它们的运算符这一规则,就可以很方便地 将逆波兰式扩充到整个算法语言。 例如,赋值语句<变量>:三<表达式>可改写为<变量><表达式 >:三; GOTO L改写成Lmp(其中jmp表示转移的运算符, L表示逆波兰式的编号或地址)
计算逆波兰式表示的算术或逻辑表达式比计算中缀式要 简单。这是因为计算逆波兰式不要比较运算符的优先级,只 要一遇到运算符就立即可以计算。具体实现中只要用一个栈 来存放运算对象,故该栈又称运算对象栈或运算分量栈。在 栈中存放未被计算的运算对象,当一旦扫描到运算符时,就 从栈中取出运算符所需的运算对象个数进行计算。然后再将 计算结果放入栈中,当全部扫描完逆波兰式后栈顶元素即为 最后的计算结果。 一般算法语言除了算术表达式和逻辑表达式外,还有其 它如赋值语句、条件语句、循环语句等,但只要遵守运算对 象后直接紧跟计算它们的运算符这一规则,就可以很方便地 将逆波兰式扩充到整个算法语言。 例如,赋值语句 <变量>:=<表达式>可改写为<变量><表达式 >:= ;GOTO L 改写成L jmp (其中jmp 表示转移的运算符, L表示逆波兰式的编号或地址)