C.6 YACC源程序例子说明
  例1用YACC描述一个交互式的计算器,该计算器有26个寄存器,分别用小写字母a到z表示,它能接受由运算符+。-。。/。%(取模)。&(按位求与)。|(按位求或)组成的表达式,能为寄存器赋值,如果计算器接受的是一个赋值语句,就不打印出结果,其它情况下都给出结果,操作数为
整数,若以0(零)开头,则作为八进制数处理。
  例1的YACC源程序见附录C.6.1。
  读者从例1中可以看出用优先关系和二义性文法能使源程序简洁,还可看到错误处理方法,但例1不足之处是它的词法分析程序太简单,还有对八进制与十进制数的区分也最好在词法分析中处理。
  例2 这个例子是例1的改进,读者能看到语义值联合类型的定义及使用方法和如何模拟语法错误并进行处理,该例也是描述一个交互式的计算器,比例1的计算器功能强,它可以处理浮点数和浮点数的区间的运算,它接受浮点常数,以及+、-、*、/、一元-和=(赋值)组成的表达式,它有26个浮点变量,用小写字母a到z表示,浮点数区间用一对浮点数表示:
  (x,y)
其中x小于或等于y,该计算器有26个浮点数区间变量,用大写字母A到Z表示。
  和例1相似,赋值语句不打印出结果,其它表达式均打印出结果,当发生错误时给出相应的信息。
  例2 的YACC源程序见附录C.6.2。
  下面简单总结例2的一些特点。
  1) 语义值联合类型的定义
  区间用一个结构表示,其成员给出区间的左右边界点,该结构用C语言的typedef语句定义,并赋与类型名INTERVAL。
  YACC的语义值栈经过%union定义后,可以存放整型。浮点及区间变量的值,还有一些函数(如hilo,vmul,vdiv)都返回结构类型的值。
  2) YACC的出错处理
  源程序中用到了yyerror来处理除数区间中含有0或除数区间端点次序倒置的错误,当碰到上述错误时,yyerror使YACC调用其错误处理程序,丢掉出错的输入行,继续处理。
  3) 使用有冲突的文法
  如果读者在机器上运行这个例子,就会发现它包含18个移进/归约冲突,26个归约/归约冲突,请看下面两个输入行:
  2.5+(3.5-4.0)
  2.5+(3.5,4.0)
在第二行中2.5用在区间表达式中,所以应把它当作区间处理,即要把它的类型由标量转换成区间量,但YACC只有当读到后面的','时才知道是
否应该进行类型转换,此时改变主意为时已晚,当然也可以在读到2.5时再向前看几个符号来决定2.5的类型,但这样实现较困难,因为YACC本身
不支持,该例是通过增加语法规则和充分利用YACC内部的二义性消除机构来解决问题的。在上述文法中,每一个区间二元运算都对应两条规则,其中一条左操作数是区间,另一条左操作数是标量,YACC可以根据上下文自动地进行类型转换。除了这种情况外,还存在着其它要求决定是否进行类型转换的情形,本例将标量表达式的语法规则放在区间表达式语法规则的前面,使运算量的类型先为标量。直到必要时再转换成区间,这样就导致了那些冲突。有兴趣的读者不妨仔细看一看这个源程序和YACC处理它时产生的y.output文件,分析一下YACC解决冲突的具体方法。
  要注意上述解决类型问题的方法带有很强的技巧性,对更复杂的问题就难以施展了。