编辑: 星野哀 2019-07-02

1、如P中下一项是操作数,压入栈顶

2、如P中下一项是n元操作符,n个参数必须是栈顶部的n个元素,计算结果并替换这n个元素.这种计值策略直接、简单,是很多翻译器中生成表达式代码的基础. 表达式的语义 (3/3) 中缀计值中缀是常见的,但用于程序语言中会导致一些问题:

1、只适合于二元操作.语言单用中缀是不够的,还需使用前缀,这二者的混合使翻译更为复杂.

2、表达式中涉及多个中缀操作时,如不使用括号,则存在固有二义性.为解决这个问题,通常引入隐含的规则. 返回 操作子计值顺序 操作的层次 即操作的优先规则 返回 同一层次操作的计算顺序的隐含规则优先级对算术表达式是适用的,因为表达式语义的数学模型已对大多数程序员熟知的. 结合律 结合律 然而,当语言引入新的,不是源自传统数学的操作符时,优先规则可能不再有用,因此,需要有不同方法来处理扩展的操作集.C语言:使用扩展的优先规则集合,大多数使用从左到右的结合律.大多数C规则是合理的.APL:操作数为数组和矢量,语言没有优先规则.所有表达式计值从右到左.这规则对大多数表达式也是合理的,除了一些典型的表达式,如a-b-c-意为a-(b-c).Smalltalk:模型类似APL,没有优先规则,表达式计值从左到右.Forth:用于实时过程控制.其运行时结构为栈,语言是纯后缀的,没有优先规则. 返回 执行时表示 对中缀形式的表达式的解码是困难的,需要翻译为易于解码的可执行形式.通常的选择有:

1、机器代码序列表达式被直接翻译成实际的机器代码,而不是先翻译为中间形式.指令顺序反映了初始表达式的顺序控制结构.机器代码序列必须使用显式的临时存储位置来保持中间结果,允许使用硬件解释器,因此,执行速度快.

2、树结构表达式可以以其自然的树结构直接执行(使用软件解释器).执行通过简单的树遍历来完成.这是LISP中使用的典型技术,整个程序被表示为树结构. 执行时表示

3、前缀或后缀形式这两种形式可用前面给出的解释算法来执行.在某些基于堆栈组织的实际计算机中,实际的机器代码表示为后缀形式.前缀表示是SNOBOL4程序的执行形式,执行从左到右进行.每个操作递归地调用解释器来计值其操作数. 表达式的树表示的计值 表达式翻成树结构虽有困难,但总体上是直接的.而树到可执行原语序列的翻译,则涉及计值顺序的一些微妙问题.在代码生成中出现的计值顺序问题有:问题一:统一的计值规则问题二:副作用,Side effect.问题三:出错条件问题四:短路布尔表达式 表达式的计值(1):统一的计值规则 对表达式树中的每个操作结点,首先计值其操作数(或生成相应代码),然而应用操作到计算出的操作数上(或生成相应代码)――积极计值规则(eager).对有些表达式来说,计值发生的顺序并不重要,可以选择以优化存储和其他机器特性.例,对(a+b)*(c-a),下列顺序均可接受.顺序一:取a的右值顺序二:取c的右值 取b的右值 取b的右值 a+b→d 取a的右值 取c右值 c-a→e c-a→e a+b→d d*e→f表达式的右值 d*e→f + Z Y

0 X X Y IF = / 表达式的计值(1):统一的计值规则 但是,并不是在任何情况下都可以使用这种统一的计值规则.例如,包含条件的表达式:Z+(Y=0?X:X/Y),当Y≠0时,计算X/Y 表达式的计值(1):统一的计值规则 采用统一规则,对IF操作,需先计算操作数,即使Y=0.显然,此时我们不希望所有操作数被计值.从而,我们有另一个规则:永不在应用操作之前计值操作数.即,总是不计值地传递操作数,由作用操作决定什么时候计值操作数――Lazy计值.然而,对此规则,在很多情况下是不实际的.比如:如何仿真未计值操作数到操作的传递?这需要软件仿真.这两种计值规则:积极和隋性(lazy),对应子程序参数传递的按值和按名.对表达式而言,没有简单的统一规则是完全令人满意的,通常规则是混合的. 表达式的计值(2):副作用 表达式中使用有副作用的操作,一直是语言设计中的争论点.考虑:a*fun(x)+a对乘法:需先取a的右值并计值fun(x)对加法:需取a的右值,并计算乘法.显然,我们希望只取a一次并应用到两个地方,而且fun(x)的计值在取a的前或后并无区别.但如fun有副作用,改变了a的值,则计值序成为关键.如a值本为1,但fun执行后值为3,并改a为2.则表达式可能值为:

下载(注:源文件不在本站服务器,都将跳转到源网站下载)
备用下载
发帖评论
相关话题
发布一个新话题