所以我一直试图用野牛解析类似哈克尔的语言语法。我将省略语法和一元减号的标准问题(例如,来自(-5)
和-5
的{{1}}是什么,或\x->x-5
是a-b
还是a-(b)
或apply a (-b)
apply a \x->x-b
本身仍然可以expression: '(' expression ')'
| expression expression /* lambda application */
| '\\' IDENTIFIER "->" expression /* lambda abstraction */
| expression '+' expression /* some operators to play with */
| expression '*' expression
| IDENTIFIER | CONSTANT /* | ..... */
;
,哈哈。)然后直接找到让我感到惊讶的事情。
为了简化整个过程,请考虑以下情况:
expression expression
我解决了与' +'的所有转变/减少冲突和' *' %左和右%
优先级宏,但我在某种程度上未能找到任何好的解决方案如何设置
expr. OPER expr.
lambda应用程序的优先级。我试过了
%precedence和%left以及%prec标记,如此处所示
%http://www.gnu.org/software/bison/manual/html_node/Non-Operators.html#Non-Operators,
但看起来野牛完全忽略了对此的任何优先设置
规则。至少我试过的所有组合都失败了。关于这一点的文档
话题很稀疏,整个事情看起来只适合处理
"经典" expression: expression_without_lambda_application
| expression expression_without_lambda_application
;
expression_without_lambda_application: /* ..operators.. */
| '(' expression ')'
;
案例。
问题:我做错了什么,或者在Bison中这是不可能的?如果不, 是不是没有支持,或者是否有一些理论上的理由为什么不呢?
备注:当然有一种简单的解决方法可以强行左折叠和 看起来像
的优先级{{1}}
......但那不尽如人意,对吧? :
谢谢!
答案 0 :(得分:3)
如果您了解LR解析的工作方式,最简单的理解野牛优先级是如何工作的,因为它基于LR算法的简单修改。 (这里,我只是结合SLR,LALR和LR语法,因为基本算法是相同的。)
LR(1)机器有两种可能的动作类别:
在LR(1)语法中,总是可以根据机器状态和先行标记做出决定。但是某些常见的结构 - 特别是中缀表达式 - 显然需要看起来比它们需要的更复杂的语法,并且需要比必要时更多的单位减少。
在一个LR解析是新的时代,大多数从业者习惯于某种运算符优先语法(见下面的定义),并且其中的周期比现在更昂贵,以便额外的单位减少似乎令人讨厌,LR算法的修改使用标准优先技术是有吸引力的。
修改 - 基于用于解析运算符优先级语法的经典算法 - 涉及为每个右侧(即每个生产)和每个终端分配优先值(整数)。然后,在构造LR机器时,如果给定状态和前瞻可以触发shift或reduce操作,则通过将可能的缩减的优先级与前瞻令牌的优先级进行比较来解决冲突。如果减少具有更高的优先权,则获胜;否则机器会移动。
请注意,减少优先级永远不会相互比较,也不是令牌优先级。它们实际上可以来自不同的领域。此外,对于一个简单的表达式语法,直观地将比较与运算符"在堆栈的顶部&#34 ;;这实际上是通过使用生产中最右边的终端来分配生产的优先级来实现的。为了处理左与右的关联性,我们实际上不使用与终端相同的优先级值。左关联产生的优先级略高于终端的优先级,右关联产生的优先级略低。这可以通过使终端优先于3的倍数并且减少优先于比终端更大或更小的值1来完成。 (实际上在实践中比较是> 而不是≥因此可以使用偶数来表示终端,但这是一个实现细节。 )
事实证明,语言并不总是那么简单。所以有时候 - 一元运算符的情况就是一个典型的例子 - 明确提供与默认值不同的简化优先级是有用的。 (另一种情况是,在多于一个的情况下,优先级与第一个终端的关系比最后一个更多。)
编辑说明: 真的,这都是黑客攻击。这是一个很好的黑客,它可能是有用的。但就像所有黑客一样,它可以推得太远。具有优先权的错综复杂的技巧,需要完全理解算法和语法的详细分析,恕我直言,优雅。他们很困惑。使用无上下文语法形式和解析器生成器的重点是简化语法的表示,使其更容易验证。 /编辑说明。
运算符优先级语法是一种运算符语法,可以仅使用优先级关系自下而上解析(使用诸如经典" shunting-yard"算法之类的算法)。运算符语法是一种语法,其中右侧没有两个连续的非终端。和制作:
expression: expression expression
不能用运算符语法表示。
在该生产中,班次减少冲突发生在中间,就在运营商出现运营商之前。在这种情况下,人们会想要将产生第一个表达式的减少的优先级与分隔表达式的不可见运算符进行比较。
在某些情况下(这需要仔细的语法分析,因此非常脆弱),可以区分可以启动expression
的终端和可能是运营商的终端。在这种情况下,可以使用FIRST expression
组中的终端优先级作为优先级比较中的比较器。由于这些终端永远不会用作运营商生产中的比较器,因此不会产生额外的歧义。
当然,只要终端可以是中缀或前缀运算符(例如一元减号),它就会失败。所以它在大多数语言中可能只是理论上的兴趣。
总之,我个人认为明确定义非应用程序表达式的解决方案清晰,优雅并且与LR解析理论一致,而任何使用优先关系的尝试都会变得不那么容易理解和验证
但是,如果你坚持,这里的语法将在这个特殊情况下 (没有一元运算符),基于为可能启动expression
的标记分配优先值:
%token IDENTIFIER CONSTANT APPLY
%left '(' ')' '\\' IDENTIFIER CONSTANT APPLY
%left '+'
%left '*'
%%
expression: '(' expression ')'
| expression expression %prec APPLY
| '\\' IDENTIFIER "->" expression
| expression '+' expression
| expression '*' expression
| IDENTIFIER | CONSTANT
;