Yacc 优先规则实际上并不是关于表达式的优先级,尽管它们可以用于此。相反,它们是一种明确解决移位/归约冲突(并且仅是移位/归约冲突)的方法。
了解它的工作原理需要了解 shift/reduce(自下而上)解析的工作原理。基本思想是您从输入中读取标记符号并将这些标记推送(“移位”)到堆栈中。当堆栈顶部的符号与语法中某些规则的右侧匹配时,您可以“减少”规则,从堆栈中弹出符号并用规则左侧的单个符号替换它们。您重复此过程,移动标记并减少规则,直到您读取整个输入并将其减少为开始符号的单个实例,此时您已成功解析整个输入。
上面的基本问题(以及解析器生成器的整个机制正在解决的问题)是知道何时减少规则以及何时移动令牌(如果两者都可能的话)。解析器生成器(yacc 或 bison)构建了一个状态机,该状态机跟踪哪些符号已被移动,因此知道哪些“部分匹配”规则当前是可能的,并将移动限制为那些可以匹配更多此类规则的标记。如果所讨论的语法不是 LALR(1),这将不起作用,因此在这种情况下,yacc/bsion 会报告 shift/reduce 或 reduce/reduce 冲突。
优先规则解决移位减少冲突的方式是通过为语法中的某些标记和规则分配优先级。每当要移动的标记和要减少的规则之间存在移位/减少冲突时,并且两者都有优先级,它将执行具有更高优先级的规则。如果它们具有相同的优先级,那么它会查看与优先级关联的%left/%right/%nonassoc 标志——%left 表示减少,%right 表示移位,%nonassoc 表示两者都不做并将其视为语法错误。
剩下的唯一棘手的一点是令牌和规则如何获得优先权。令牌从它们所在的 %left/%right/%nonassoc 指令中获取它们,该指令也设置了排序。规则从 %prec 指令或从其右侧最右侧的终端获得优先级。所以当你有:
%left '*'
%left '+'
expr: expr '+' expr
| expr '*' expr
;
您正在使用%left 指令设置'*' 和'+' 的优先级,这两个规则从这些令牌中获得优先级。
当你有:
%left MULTIPLY
%left PLUS
expr: expr '+' expr %prec PLUS
| expr '*' expr %prec MULTIPLY
;
您正在设置标记 MULTIPLY 和 PLUS 的优先级,然后显式设置规则以具有这些优先级。但是,您没有为标记 '*' 和 '+' 设置任何优先级。因此,当两个规则之一与'*' 或'+' 之间存在移位/减少冲突时,优先级不会解决它,因为令牌没有优先级。