如何解决这种模棱两可的语法?

时间:2015-10-22 16:41:21

标签: bison yacc flex-lexer context-free-grammar ambiguous-grammar

我正在创建一个编译器,我正在使用lex和bison。

这是我语法的一部分:

math
    : math binop math
    | VAR
    | INT
    | GREEK
    | "(" math ")"
    | math comp math
    | "-" math
    | math math
    | "\\sqrt" "{" math "}"
;

我已将上述内容更改为此但增加了减少/减少错误的数量。它减少了移位/减少错误的数量。

math
    : math binop element
    | VAR
    | INT
    | GREEK
    | "(" math ")"
    | math comp element
    | "-" element
    | math element
    | "\\sqrt" "{" element "}"
;


element
    : VAR
    | INT
    | GREEK
    | math
;

有没有办法让它们都减少?

谢谢!

1 个答案:

答案 0 :(得分:0)

你已经朝着正确的方向迈出了一步,但也许最好退一步思考事情的开始,以及这将如何适用于一个实际的表达。

让我们考虑像a + b + c这样的表达式。在原始语法中,您有math binop math。鉴于a+b+c,它应该将其视为amath+binopb+c是另一个math或者它应该将其视为a+b是数学,+binop,而c是数学(然后math命名a+b进一步细分为a+b)?从略微不同的角度来看,a + b + c应该被解析为(a + b) + c还是a + (b + c)?在+的情况下,它并没有产生任何真正的区别 - 但是对于-(对于一个明显的例子)它确实有所作为。

根据你的第一个语法,任何一个都是可以接受的。事实上,即使是你的第二个,也可以接受(但是通过使element ......从属于math,你隐含地给了yacc关于哪个方向的指导解析它。)

这将我们带到下一点:在什么情况下(你认为)需要使用math binop math制作?如果我们定义math来解析任意复杂度的表达式,element是否可以限制为基本上单个操作数?如果我们这样做,那么像a + b + c这样的每个表达式都必须解析为math binop operand,对吗?在这种情况下没有歧义(a+b部分会进一步解析为operand binop operand

这留下了一个相当基本的问题。我们如何处理不同运营商的优先级?例如,至少在通常的方案中,我们希望*/的优先级高于+-

在yacc之类的东西中,至少有两种根本不同的处理方法。一个是语法本身,通过定义几个不同类型的子表达式:

add_expr : mul_expr '+' mul_expr
         | mul_expr '-' mul_expr
         ;

mul_expr : factor '*' factor
         | factor '/' factor
         ;

另一种是在指令中设置优先级,例如:

%left '+' '-'
%left '*' '/'
%left UNARYMINUS

这让我们让语法本身不明确,但随后告诉解析器生成器如何解决歧义。

所以,使用这个,我们可以有类似的东西:

expr : expr '+' operand
     | expr '-' operand
     | expr '*' operand
     | expr '/' operand
     ;

operand: VAR
       | INT
       | '(' expr ')'
       | '-' expr %prec UNARYMINUS
       ;

最后一位(%prec UNARYMINUS)告诉它将-(在这种情况下)视为具有我们上面为UNARYMINUS指定的优先级(我们已定义的最高优先级,因为它是列表中的最后一个。)

我还没有尝试覆盖你的整个语法,但我认为至少涵盖了你可能需要(或者至少想要)应用于删除大多数语法的大部分基本转换含糊不清。可能还值得注意的是,转移/减少冲突通常是相当无害的。解析器生成器为这种情况提供的解析通常可以正常工作,并且在某些情况下,这种模糊的语法实际上会比解决所有歧义的语法更有效,因此相当数量的语法不会尝试修复所有这些。