What makes Java easier to parse than C?
我知道C和C++的语法是上下文敏感的,特别是在C中需要一个"Lister-HACK"。另一方面,我的印象是,尽管两种语言有很大的相似性,但是只需2个令牌就可以解析Java。
为了使C更易于解析,您需要对它做什么更改?
我问是因为我看到的所有关于C的上下文敏感性的例子在技术上都是允许的,但是非常奇怪。例如,
可以用参数a调用void函数foo。或者,它可以声明a是foo类型的对象,但您也可以很容易地摆脱偏执。部分原因是C语法的"直接声明器"生成规则实现了同时声明函数和变量的双重目的。
另一方面,Java语法对于变量声明和函数声明有单独的生成规则。如果你写信
号
然后您知道这是一个变量声明,并且可以将foo明确地解析为一个类型名。如果类foo没有在当前作用域的某个地方定义,那么这可能不是有效的代码,但这是一项语义分析工作,可以在以后的编译器过程中执行。
我曾经看到过,由于TyWIFF,C很难解析,但是您也可以在Java中声明自己的类型。除了direct_declarator外,还有哪些C语法规则是错误的?
- 很酷的问题。可能太过宽泛,或者主要是固执己见。
- 这是一个关于解析器的有效问题,唯一宽泛的或基于它的观点是最后几个句子(可能应该删除或更改)。以接近的票数退出。
- 我相应地编辑了这个问题,感谢@r.。为了反馈。
- 事实上,每一种(标准)计算机语言都是上下文敏感的;您不能声明一种类型的变量,而且大多数语言都会被滥用。这不同于"语言的所有语法"是上下文敏感的;大多数构建解析器的人构建一个上下文无关(甚至更严格)的解析器,然后在解析器之外使用黑客检查上下文无关的属性。
- @伊拉巴克斯特,我不会称之为"黑客"。将问题一分为二似乎是一件合理的事情,因为解析上下文敏感的语言是不可能有效地完成的(事实上,甚至解析上下文无关的语言也是不高效的,这就是为什么我们通常只局限于上下文无关的子集)。一个上下文无关的分析+静态分析,只检查AST上的上下文敏感属性,这是一个合理的做法。
- 顺便说一下,我相信Knuth证明了带有klookahead标记的语言实际上等同于带有1lookahead标记的语言,即LL(k)与LL(1)是同一组语言(尽管不确定这个结果是否对LR和LALR语法有效)。事实上,标准或某些编译器不使用仅带有1先行标记的语法,这不是语言本身的问题,而是实现的问题。
- 全部:"检查上下文无关属性"应为"检查上下文相关属性"。
- @巴库鲁:我称它们为"黑客",因为它们不是解析机器的一部分,而且没有明显的结构。如果你更喜欢称它们为"特殊静态分析",我可以接受。我同意将两个问题分为两个问题是正确的做法;我指出在大多数情况下都是这样。关于"高效"解析:实际上,GLR在全上下文无关的解析程序上做得很好;我有40多个主流语言使用的语法(您可以构造一些晦涩的上下文无关的语法,这些语法很难实现,但它们在现实世界中并不存在)。
- @巴库鲁:我也相信有人(克努斯?)证明了这一点。我认为从lr(k)等结构中构造lr(1)会破坏语法的大小,这使得将lr(1)用于lr(k)是不现实的。而且,这不处理需要任意前瞻的上下文无关语法,这是常见的。GLR可以相当有效地完成所有这些工作,而不会使您扭曲概念上简单的语法。
解析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++应用程序中投入太多,为了便于构建解析器的人进行切换;此外,他们的痛苦已经结束,现有的解析器工作得很好。
您可能需要编写自己的解析器。好吧,那很好;只是不要期望社区的其他人让你改变他们必须使用的语言,使你更容易。他们都希望这对他们来说更容易,那就是使用文档化和实现的语言。
- 回答得好。另请参见D和C+,它们试图解决其中一些问题。S/内容/竞争/
- @pfx:我知道d;它也试图清理langauge语义。不知道C+。要知道,尽管存在这些,但是C++AS是社区似乎没有任何灭绝的危险。
- 我以前读过分析后的生活,发现它真的让人大开眼界;它让我明白,语义分析(名称/类型解析,…)中的工作比分析中的要多得多。我不想改变任何语言的语法。我想了解一种语言的属性是什么,在这种语言中,您可以先进行句法分析,然后进行语义分析。C不是这样的语言(需要词法破解);我一直认为Java是我想知道的原因。
- @ Korrok:阅读我关于用GLR解析器构建爪哇/C++的答案。你不需要任何雷克斯黑客。所以,区别就在使用错误解析技术的人的头脑中。…当然,构建一个完整的C++前端(特别是C++ 14,我们已经做了)比Java8更难,但是它们都很难(在努力方面,关注细节),解析是最容易的一部分。
- @Korok:我已经构建了一个完整的商业编译器(以及一些小的编译器)。人们问我为什么不使用yacc或lex,我的回答是:为什么要麻烦?词法分析和解析相比下一步要容易得多,我从来没见过有什么需要。听艾拉说:他说的是真的。
- RE:"总是有一个问题,决定‘……’是否关闭两个模板,或者是一个‘右移运算符’":这在Listar中是很难做到的,但是如果Listar发出EDCOX1 0Ω,那么解析器就直接把它转换成EDCOX1,1,EDCOX1,1,如果需要,因为在Java中(不同于C++),TE的参数是MPlate总是类型,而不是值。更棘手的例子是类似于foo = ( bar <,我们可能需要大量的前瞻性来区分(例如)foo = (bar) baz;中的解析和(例如)foo = (bar < baz) ? 0 : 1;中的解析。
- @ Ruakh:当遇到">"时,我们的Java词典所做的是发出两个令牌">">。shift操作符是串行遇到的这两个令牌,语义检查第二个令牌从第一个令牌之后的列中开始。所以解析器不需要"转换"a">>"令牌。我们通过使用一个GLR解析器来处理(本地)的歧义,该解析器很乐意(高效地)探索所有可能的有效解析器,以找到实际工作的解析器。
- @大卫:谢谢你的信任投票。我们也不使用lex或yacc,但原因完全不同。我猜您使用的是递归下降的手工编写的解析器;这些解析器相对容易构建,更重要的是容易弯曲以处理奇怪的解析问题。我们使用glr和纯bnf语法,不必弯曲解析器,而是使用额外的语义检查来消除不适当的缩减(请参阅我上面关于">">"的注释)。事实证明,我们的C++前端完全是由C++标准中的语法派生而来的。…
- @大卫.pfx…使用真正的语法可以让我们为"超越解析的生活"做许多其他有趣的事情,例如通过使用基本语言语法编码的显式属性语法来构建符号表、构造控制图和数据流图。因此,通过不使用递归下降,我们可以为前端分析器的后面部分获得额外的帮助。
- 有趣。我不知道运营商和仿制药可以出现在爪哇的同一个地方。
- 啊,我刚意识到你在说莱克斯。
- 我同意你的"解析后的生活":例如,C中的过载分辨率可以编码任何3-SAT问题,因此是NP困难的。
- @J&246;rgwmittag:有什么3sat问题吗?真的。你有这个事实的参考资料吗?
- blogs.msdn.com/b/ericlippert/archive/2007/03/28/&hellip;
- @ IraBaxter:我们中的很多人无法构建一个C++编译器,而在其他地方,很少有语言需要一个LR解析器。我的愿望如果是工具,使简单的语言如此琐碎的编译,每个应用程序将有一两个编译器。但那将是另一个问题…
- C不含糊。没有上下文,它是模棱两可的。这不会使它"固有的模棱两可"。
- C语言不含糊。Afaik,所有上下文无关的语法都是。