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
显式设置规则以具有这些优先级。但是,您没有为令牌'*'
和'+'
. '*'
因此,当两个规则之一与or之间存在移位/减少冲突时'+'
,优先级不会解决它,因为令牌没有优先级。