假设此代码有效:
left '*'
left '+'
expr: expr '+' expr
| expr '*' expr
;
我想定义另一个优先级标记,如:
left MULTIPLY
left PLUS
expr: expr '+' expr %prec PLUS
| expr '*' expr %prec MULTIPLY
;
然而,这实际上并没有效果。
我认为这两种形式应该是等价的,但是,它们不是。
这不是实际问题。我只是想知道这种现象的原因和原理。
感谢。
答案 0 :(得分:3)
你说你并没有试图解决一个具体的实际问题。从你的问题来看,我对你如何使用优先级标记感到有点困惑。
我认为你会发现你不需要经常使用优先级标记。阅读器通常更简单,更清晰,重写语法,以便明确说明优先级。为了得到乘法和除以加法和减法的优先级,你可以做这样的事情(例子改编自John Levine, lex& yacc 2 / e,1992):
%token NAME NUMBER
%%
stmt : NAME '=' expr
| expr
;
expr : expr '+' term
| expr '-' term
| term
;
term : term '*' factor
| term '/' factor
| factor
;
factor : '(' expr ')'
| '-' factor
| NUMBER
;
在您的示例中,PLUS
和MULTIPLY
不是真正的令牌;您无法与'+'
和'*'
互换使用它们。莱文称他们为伪代币。他们将您的作品链接回您使用%left
和%nonassoc
声明定义的优先顺序列表。他给出了这个例子,说明即使' - '标记的优先级较低,你可以使用%prec
给出一元减去高的优先级:
%token NAME NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%%
stmt : NAME '=' expr
| expr
;
expr : expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| '-' expr %prec UMINUS
| '(' expr ')'
| NUMBER
;
总结一下,我建议遵循我的第一个代码示例的模式,而不是第二个;使语法明确。
答案 1 :(得分:2)
Yacc优先级规则并不是关于表达式的优先级,尽管它们可以用于表达式。相反,它们是一种明确解决转移/减少冲突(并且只能转移/减少冲突)的方法。
了解它的工作原理需要了解shift / reduce(自下而上)解析的工作原理。基本思想是从输入中读取令牌符号并将这些令牌推送(“移位”)到堆栈上。当堆栈顶部的符号与语法中某些规则的右侧匹配时,您可以“缩小”规则,从堆栈中弹出符号并将其替换为规则左侧的单个符号。您重复此过程,转移标记并减少规则,直到您已读取整个输入并将其缩减为单个开始符号实例,此时您已成功解析整个输入。
上面的基本问题(以及解析器生成器正在解决的问题)是知道何时减少规则与何时移动令牌(如果两者都可能)。解析器生成器(yacc或bison)构建一个状态机,用于跟踪哪些符号已被移位,因此知道当前可能存在哪些“部分匹配”规则,并将移位限制为可以匹配更多此类规则的令牌。如果所讨论的语法不是LALR(1),则这不起作用,因此在这种情况下,yacc / bsion报告会改变/减少或减少/减少冲突。
该优先规则用于解决shift减少冲突的方法是为语法中的某些标记和规则分配优先级。每当要移位的令牌和要减少的规则之间存在移位/减少冲突,并且两者都具有优先权时,它将执行具有更高优先级的任何一个。如果他们具有相同的优先权,那么它会查看与优先级相关联的%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
的优先级,然后明确设置规则以具有这些优先级。但是,您没有为令牌'*'
和'+'
设置任何优先顺序。因此,当两个规则之一与'*'
或'+'
之间发生转换/减少冲突时,优先级无法解决它,因为令牌没有优先权。
答案 2 :(得分:0)
Shift-reduce冲突是尝试减少生产与转移令牌和迁移到嵌套状态之间的冲突。当Bison解决冲突时,它不会比较两个规则并选择其中一个 - 它要比较一个要减少的规则和要在其他规则中移动的令牌。如果您有两个要转移的规则,这可能会更清楚:
expr: expr '+' expr
| expr '*' expr
| expr '*' '*' expr
这一点令人困惑的原因是Bison给予“reduce”规则优先级的方式是将它与一个令牌(默认情况下规则中的最后一个终端或来自prec声明的令牌)相关联然后它使用优先级表将该标记与您尝试移动的标记进行比较。基本上,预先声明只对冲突的“减少”部分有意义,并且不计入轮班部分。
查看此内容的一种方法是使用以下语法
command: IF '(' expr ')' command %prec NOELSE
: IF '(' expr ')' command ELSE command
在此语法中,您需要在缩小第一个规则或移动ELSE令牌之间进行选择。您可以通过优先使用')'标记和ELSE标记,或者使用预先声明并为NOELSE而不是')'赋予优先级。如果你试图给第二个预先声明,它将被忽略,Bison将继续尝试在优先级表中寻找ELSE标记的优先级。