语法与算子相关性之间的关系

时间:2011-05-27 06:39:09

标签: parsing operator-keyword associativity

一些编译器书籍/文章/论文谈论语法的设计及其运算符的关联性的关系。我是自上而下的忠实粉丝,特别是递归下降,解析器和迄今为止我编写的大多数(如果不是全部)编译器使用以下表达式语法:

Expr   ::= Term { ( "+" | "-" ) Term }
Term   ::= Factor { ( "*" | "/" ) Factor }
Factor ::= INTEGER | "(" Expr ")"

这是此BNF的EBNF表示:

Expr  ::= Term Expr'
Expr' ::= ( "+" | "-" ) Term Expr' | ε
Term  ::= Factor Term'
Term' ::= ( "*" | "/" ) Factor Term' | ε
Factor = INTEGER | "(" Expr ")"

根据我所读到的,有些人认为这个语法是“错误的”,因为操作员关联性的变化(这4个操作符从左到右)由不断增长的解析树向右而不是向左证明。对于通过属性语法实现的解析器,这可能是正确的,因为l-attribute值要求首先创建此值,然后传递给子节点。然而,当使用普通的递归下降解析器实现时,由我决定是先构造此节点然后传递给子节点(自上而下)还是先创建子节点然后将返回的值添加为此节点的子节点(通过在这个节点的构造函数中)(自下而上)。我应该在这里找到一些东西,因为我不同意这句话说这个语法是“错误的”,而且这种语法已被用于许多语言中。 Wirthian的。通常(或全部?)表示它的读数会促进LR解析而不是LL。

2 个答案:

答案 0 :(得分:1)

我认为这里的问题是语言有一个抽象语法就像:

E ::= E + E | E - E | E * E | E / E | Int | (E)

但实际上这是通过具体语法实现的,该语法用于指定关联性和优先级。所以,如果你正在编写一个递归的正确解析,你就会隐含地将具体语法写入其中并且这很好,尽管将它指定为一个短语结构语法也可能是好的!

如果要成为一个完全成熟的具体语法,你的语法会有几个问题。首先,你需要添加产品才能“进入下一级别”,这样可以放松一下你的语法:

Expr ::= Term + Term | Term - Term | Term
Term ::= Factor * Factor | Factor / Factor | Factor
Factor ::= INTEGER | (Expr)

否则,无法从起始符号(在本例中为Expr)中派生有效句子。例如,如果没有这些额外的制作,你会如何得出'1 * 2'?

Expr -> Term
     -> Factor * Factor
     -> 1 * Factor
     -> 1 * 2

我们可以看到其他语法以稍微不同的方式处理它:

Expr -> Term Expr'
     -> Factor Term' Expr'
     -> 1 Term' Expr'
     -> 1 * Factor Term' Expr'
     -> 1 * 2 Term' Expr'
     -> 1 * 2 ε Expr'
     -> 1 * 2 ε ε
      = 1 * 2

但这可以达到同样的效果。

您的解析器实际上是非关联的。要看到这个,请问如何解析E + E + E并发现它不能解析。无论先使用哪个+,我们会在一方获得E而在另一方获得E + E,但之后我们会尝试将E + E解析为Term这是不可能的。同样地,考虑从起始符号派生该表达式,再次不可能。

Expr -> Term + Term
     -> ? (can't get another + in here)

另一个语法是左关联的ebcase,可以导出E + E + ... + E任意长的sting。

所以总而言之,你在编写RDP时,你可以实现你喜欢的抽象语法的具体版本,你可能知道关于这一点比我更多。但是在尝试生成准确描述您的RDP的语法时存在这些问题。希望有所帮助!

答案 1 :(得分:1)

要获取关联树,您确实需要将运算符形成的树作为子树根节点,子节点具有相似的根。

您的实施语法:

Expr  ::= Term Expr'
Expr' ::= ( "+" | "-" ) Term Expr' | ε
Term  ::= Factor Term'
Term' ::= ( "*" | "/" ) Factor Term' | ε
Factor ::= INTEGER | "(" Expr ")"

必须让那个尴尬;如果你对此实现递归下降,则Expr'例程无法访问“left child”,因此无法构建树。您总是可以通过传递碎片(在这种情况下,将树部件传递给递归)来补丁,但这看起来很尴尬。你可以选择这个作为语法:

Expr  ::= Term  ( ("+"|"-") Term )*;
Term  ::= Factor ( ( "*" | "/" ) Factor )* ;
Factor ::= INTEGER | "(" Expr ")"

对于递归下降的代码同样容易(更容易?),但现在您可以毫无困难地形成所需的树。

这不会真正让你联想到;它只是塑造树木,以便它可以被允许。关联性意味着树(+(+ a b)c)与(+ a(+ b c))意思相同;它实际上是一个语义属性(肯定不适用于“ - ”,但所提出的语法无法区分)。

我们有一个工具(DMS Software Reengineering Toolkit),其中包括解析器术语重写(使用源到源转换),其中明确表达了关联性。我们写你的语法:

Expr  ::= Term ;
[Associative Commutative] Expr  ::= Expr "+" Term ;
Expr  ::= Expr "-" Term ;
Term  ::= Factor ;
[Associative Commutative] Term  ::= Term "*" Factor ;
Term  ::= Term "/" Factor ;
Factor ::= INTEGER ;
Factor ::= "(" Expr ")" ;

语法似乎更长,更笨拙,但实际上它允许我们打破特殊情况并根据需要标记它们。特别是,我们现在可以区分关联的运算符和不相关的运算符,并相应地标记它们。通过该语义标记,我们的树重写引擎自动解释了关联性和交换性。您可以使用明确的重写规则来查看full example of such DMS rules being used to symbolically simplify high-school algebra,而不是考虑这些语义属性的典型表达式语法。它内置于重写引擎中。