快乐的依赖于上下文的运算符优先级

时间:2017-01-24 19:14:02

标签: parsing haskell happy

我在这里有两个Happy代码片段,一个使用普通优先级规则,另一个使用依赖于上下文的优先级规则(两者都描述为here)。

正常:

%left '+'
%left '*'
%%

Exp :: { Exp }
    : Exp '+' Exp { Plus $1 $3 }
    | Exp '*' Exp { Times $1 $3 }
    | var         { Var $1 }

上下文相关:

%left PLUS
%left TIMES
%%

Exp :: { Exp }
    : Exp '+' Exp %prec PLUS  { Plus $1 $3 }
    | Exp '*' Exp %prec TIMES { Times $1 $3 }
    | var                     { Var $1 }

鉴于输入:

a * b + c * d

正常版本提供:

Plus (Times (Var "a") (Var "b")) (Times (Var "c") (Var "d"))

而依赖于上下文的版本给出:

Times (Var "a") (Plus (Var "b") (Times (Var "c") (Var "c")))

这些都不应该给出相同的输出吗?我在这做错了什么让他们生成不同的解析树?

1 个答案:

答案 0 :(得分:4)

“依赖于上下文的优先级”是描述该功能的一种非常误导性的方式。但是,前一节中对优先级算法的描述很准确。

正如它所说,优先级比较总是在生产(可以减少)和终端(可以转移)之间。设计优先声明语法的决定经常使这个简单的事实蒙上阴影,好像优先级只是终端的一个属性。

除非存在%prec的明确声明,否则通过复制生产中最后一个终端的优先级来设置生产的优先级。或者换句话说,生产的precdence设置为%prec子句,默认为最后一个令牌的优先级。无论哪种方式,您只能通过说它与某个终端的优先级相同来定义生产的优先级。由于这并不总是方便,解析器生成器为您提供了使用任意名称的选项,该名称不是语法符号的名称。实现是将名称视为终端,并忽略它从未在任何语法规则中实际使用的事实,但从逻辑上讲,它是要分配给该特定产品的优先级的名称。

在第一个示例中,您将制作默认优先于每个制作中的最后一个(实际上是唯一的)终端。但是在第二个示例中,您已经定义了两个命名优先级,PLUS和TIMES,并使用它们来设置两个产生的优先级。但是你没有声明任何终端的优先级。因此,当解析器生成器尝试检查可以减少的生产的相对优先级和可以移位的终端时,它发现只有其中一个具有声明的优先级。在这种情况下,它总是会改变。