是什么让Java比C更容易解析?

What makes Java easier to parse than C?

我知道C和C++的语法是上下文敏感的,特别是在C中需要一个"Lister-HACK"。另一方面,我的印象是,尽管两种语言有很大的相似性,但是只需2个令牌就可以解析Java。

为了使C更易于解析,您需要对它做什么更改?

我问是因为我看到的所有关于C的上下文敏感性的例子在技术上都是允许的,但是非常奇怪。例如,

1
foo (a);

可以用参数a调用void函数foo。或者,它可以声明afoo类型的对象,但您也可以很容易地摆脱偏执。部分原因是C语法的"直接声明器"生成规则实现了同时声明函数和变量的双重目的。

另一方面,Java语法对于变量声明和函数声明有单独的生成规则。如果你写信

1
foo a;

然后您知道这是一个变量声明,并且可以将foo明确地解析为一个类型名。如果类foo没有在当前作用域的某个地方定义,那么这可能不是有效的代码,但这是一项语义分析工作,可以在以后的编译器过程中执行。

我曾经看到过,由于TyWIFF,C很难解析,但是您也可以在Java中声明自己的类型。除了direct_declarator外,还有哪些C语法规则是错误的?


解析C++变得越来越难了。解析Java正变得同样困难。

看到这样的答案,讨论为什么C(和C++)是"硬"解析。简短的总结是C和C++语法本质上是含糊不清的,它们会给你多个解析,并且你必须使用上下文来解决歧义。然后人们会犯错误,认为你必须在解析过程中解决歧义;而不是这样,请参见下文。如果您坚持在解析过程中解决歧义,那么您的解析器将变得更复杂,并且更难构建;但这种复杂性是一种自我造成的伤害。

IIRC,Java 1.4的"明显"LALR(1)语法不含糊,因此解析起来很容易。我不确定现代Java没有至少有长距离的局部模糊性,总是存在的问题是判断"…>"是否关闭了两个模板,或者是"右移运算符"。我怀疑现代Java不再与LALR(1)进行解析。

但是,对于两种语言,都可以通过使用强解析器(或弱解析器和上下文集合HAG作为C和C++前端来完成)来解决解析问题。C和C++具有预处理器的附加复杂度,它们在实践中比看起来更复杂。一种说法是C和C++解析器很难,它们必须手工编写。这不是真的,你可以用GLR解析器生成器来构建Java和C++解析器。

但是解析并不是问题所在。

一旦解析,您将希望对ast/parse树执行一些操作。在实践中,您需要知道每个标识符的定义和使用位置("名称和类型解析",草率,构建符号表)。事实证明,这比让解析器正确地工作要多得多,因为继承、接口、重载和模板会使这一切复杂化,而且所有这些的语义都是用非正式的自然语言编写的,并且分布在语言标准的数十到数百页之间,这让人困惑。这里的C++非常糟糕。从这个角度来看,Java 7和8变得非常糟糕。(符号表并不是你所需要的;请看我的个人简历,看一篇关于"解析后的生活"的长篇文章)。

大多数人都在与纯粹的解析部分作斗争(通常永远不会完成;请检查它本身,了解有关如何为真正的语言构建工作的解析器的许多问题),这样他们就永远不会看到解析之后的生活。然后我们得到了关于什么是难以解析的民间定理,并且没有关于在那个阶段之后发生的事情的信号。

固定C++语法不会让你得到任何地方。

关于改变C++语法:你会发现你需要修补很多地方来处理任何C++语法中的局部歧义和真实歧义。如果您坚持,下面的列表可能是一个很好的起点。我争辩说,如果你不是C++标准委员会,这样做没有意义;如果你这样做了,并且使用它建立了编译器,没有人明智的使用它。在现有的C++应用程序中投入太多,为了便于构建解析器的人进行切换;此外,他们的痛苦已经结束,现有的解析器工作得很好。

您可能需要编写自己的解析器。好吧,那很好;只是不要期望社区的其他人让你改变他们必须使用的语言,使你更容易。他们都希望这对他们来说更容易,那就是使用文档化和实现的语言。