Java、C++、C#等编程语言如何解决尖括号<和>的语法歧义?

29

我曾经认为C++是一种"奇怪"的语言,因为它在使用<>时存在许多模糊不清的地方。但是,当我尝试实现一个解析器后,我发现了一个例子,这个例子几乎破坏了每一种使用<>作为泛型类型的语言:

f(g<h, i>(j));

这段代码的语法可以被解释为一个普通的方法调用(g),也可以被解释为给f赋予两个比较的结果。

这些语言(尤其是Java, 我认为Java应该是LALR(1)可解析的?)如何避免这种语法的歧义性呢?

我无法想象任何非hacky/context-free的处理方式,而且我对于这种语言如何是context-free,更不用说LALR(1)-可解析了,感到困惑...

(值得注意的是,即使是GLR解析器也无法在没有上下文信息的情况下返回这个语句的单一解析!)


2
+1 很好的问题。只是需要注意的是,Java编译器有很多启发式方法来解决歧义,它猜测得很多。他们自己承认他们的通用系统存在缺陷,并且有些情况下会失败(尽管这种情况很少),看看Java 8功能演示,他们解释了一些问题以及如何在Java 8中解决它们。 - Benjamin Gruenbaum
2
这并不含糊,它取决于g被声明为什么。 - NimChimpsky
2
@NimChimpsky:是的,这就是重点——它在语法上是有歧义的(因为它取决于上下文),但在语义上不是有歧义的(因为在给定的上下文中,它是明确的)。 - user541686
1
@Mehrdad 哈哈,这是一只会说话的猴子的名字,以 Noam 命名。 - NimChimpsky
GLR的目的是为您提供上下文无关语言的所有有效句法解析。它可以轻松解析您的示例,并生成两个解析结果。要使用此结果,您必须决定哪些模棱两可的选择是有意义的,使用除此特定形式之外的其他东西(例如,“g”的实际类型信息)。这就是我们使用工具解决问题的方式,它很好地将解析与名称和类型解析隔离开来。正如其他人所指出的,Java在遥远的星系中曾经是LALR(1)。 - Ira Baxter
显示剩余5条评论
2个回答

4
在Java中,一个通用的方法调用是<h,i>g(j),因此没有歧义 :)

哇,你说得对...我以为它会是g<h, i>(j),但显然不是! +1 很棒的答案,谢谢 :) - user541686
这也是我的反应。我认为这样做可以更容易地进行解析,但必须承认这是一种非常丑陋的hack。语言设计师不应该听取编译器人员的抱怨,因为这会导致出现这样的问题。 - Ira Baxter

2
“我无法想象任何非hacky/无上下文方式来处理这个问题,我对于任何这样的语言如何能够是上下文无关的,更不用说LALR(1)可解析的了...”答案是它们并不是(至少Java和C++不是,我对C#知之甚少)。你提供的Java语法可以追溯到1996年,当时还没有引入泛型。有关进一步讨论,请参见 C#和Java语法是否为LALR(x)?

即使是 C 语言也不是完全的上下文无关文法,因为一个符号的使用可以表示标识符或类型名称,这取决于声明,而声明可以与使用相隔任意数量的代码。 - ebohlman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接