我熟悉the grammars of C and C++ are context-sensitive的事实,特别是你需要一个" lexer hack"另一方面,尽管两种语言之间存在相当大的相似性,但我的印象是你只能使用2个前瞻标记来解析Java。
您需要更改哪些内容才能使其更易于解析?
我问,因为我所看到的所有关于C&C的上下文敏感性的例子在技术上是允许的,但非常奇怪。例如,
foo (a);
可以使用参数foo
调用void函数a
。或者,它可以声明a
是foo
类型的对象,但您可以轻松地摆脱这些问题。在某种程度上,这种怪异的发生是因为"直接声明者" C grammar的生成规则实现了声明函数和变量的双重目的。
另一方面,Java grammar具有变量声明和函数声明的单独生成规则。如果你写
foo a;
然后你知道它是一个变量声明,foo
可以明确地解析为一个类型名。如果类foo
尚未在当前范围内的某处定义,那么这可能不是有效代码,但这是可以在稍后的编译器传递中执行的语义分析的工作。
我已经看到它因为typedef而难以解析C,但你也可以在Java中声明自己的类型。除了direct_declarator
之外,哪个C语法规则有问题?
答案 0 :(得分:76)
解析C ++变得越来越难。解析Java变得同样困难。
见SO answer discussing why C (and C++) is "hard" to parse。简短的总结是C和C ++ 语法本质上是模棱两可的;他们会给你多个解析,你必须使用上下文来解决歧义。然后人们会假设你在解析时必须解决歧义;不是这样,见下文。如果你在解析时坚持解决歧义,你的解析器会变得更复杂,而且构建起来要困难得多;但这种复杂性是一种自我伤害。
IIRC,Java 1.4的“明显的”LALR(1)语法并不含糊,因此解析起来很“容易”。我不太确定现代Java至少没有长距离的局部模糊;总是存在决定“...>>”的问题关闭两个模板或是“右移操作员”。我怀疑modern Java does not parse with LALR(1) anymore。但是,对于这两种语言,人们可以通过使用强解析器(或弱解析器和上下文收集黑客,因为C和C ++前端主要执行此操作)来解决问题。 C和C ++具有预处理器的额外复杂性;这些在实践中比它们看起来更复杂。一种说法是C和C ++解析器非常难以完成,必须手工编写。 It isn't true; you can build Java and C++ parsers just fine with GLR parser generators.
但解析并不是问题所在。
解析后,您将需要对AST / parse树执行某些操作。实际上,您需要知道每个标识符的定义是什么以及它的使用位置(“名称和类型分辨率”,粗略地构建符号表)。事实证明,这比通过继承,接口,重载和模板更加复杂,并且由于所有这些语义都是用非正式的自然语言编写,这些语言分布在数十到数百页之间,这使得它更加困难。语言标准。 C ++在这里真的很糟糕。从这个角度来看,Java 7和8变得非常糟糕。 (而且符号表并不是你所需要的;请参阅我的生物文章,阅读更长篇文章“解析后的生活”)。
大多数人都在与纯粹的解析部分斗争(通常永远不会完成;检查SO本身是否有许多关于如何为实际语言构建工作解析器的问题),所以他们在解析后看不到生命。然后我们得到关于难以解析的内容的民间定理,并且没有关于该阶段之后发生的事情的信号。
修复C ++语法无法让你随时随地。
关于更改C ++语法:你会发现你需要修补很多地方来处理任何C ++语法中的各种局部和真实歧义。如果你坚持,following list might be a good starting place。我认为,如果你不是C ++标准委员会,这样做没有意义;如果你这样做,并使用它构建了一个编译器,没有人会理智地使用它。为了方便构建解析器的人们,现有的C ++应用程序投入太多了。此外,他们的痛苦已经结束,现有的解析器工作正常。
您可能想要编写自己的解析器。好没关系;只是不要指望社区的其他人让你改变他们必须使用的语言,以使你更容易。他们都希望他们更容易,这就是使用记录和实施的语言。