解析C ++的复杂性

时间:2010-11-13 11:44:28

标签: c++ parsing theory big-o compiler-theory

出于好奇,我想知道解析C ++的“理论”结果是什么。

设n为我项目的大小(例如,在LOC中,但由于我们将处理big-O,因此不是很重要)

  • C ++是否在O(n)中解析?如果不是,那复杂性是多少?
  • 在O(n)中解析C(或Java或其语法意义上的任何简单语言)?
  • C ++ 1x会引入更难以解析的新功能吗?

参考资料将不胜感激!

3 个答案:

答案 0 :(得分:17)

我认为“解析”这个术语正在被不同的人以不同的方式解释为问题。

从狭义的技术意义上讲,解析只是验证源代码是否与语法匹配(或者甚至构建树)。

有一个相当普遍的民间定理,说你根本无法解析C ++(在这个意义上),因为你必须解析某些符号的含义才能解析。民间定理完全错了。

它来自于使用“弱”(LALR或回溯递归下降)解析器,如果它们承诺错误选择几个可能的文本局部模糊部分(这个SO thread discusses an example)的子列,完全失败,因为有时做出这样的选择。使用这种解析器解决困境的方式是在解析发生时收集符号表数据,并将额外的检查混合到解析过程中以迫使解析器在遇到此类选择时做出正确的选择。这样做的代价是通过解析显着纠缠名称和类型解析,这使得构建此类解析器非常困难。但是,至少对于传统的GCC,他们使用LALR,这是解析的线性时间,如果你包括解析器所做的名称/类型捕获,我认为不会那么昂贵(解析之后还有更多的事要做,因为我不喜欢我认为他们都做到了。)

至少有两种使用“纯”GLR parsing技术完成的C ++解析器实现,它只是承认解析可能是本地模糊的,并且捕获多个解析而没有评论或显着的开销。 GLR解析器是线性时间,没有局部模糊。它们在模糊区域中更昂贵,但实际上,标准C ++程序中的大多数源文本都属于“线性时间”部分。所以有效率是线性的,甚至可以捕捉到模糊性。两个实现的解析器在解析后都进行名称和类型解析,并使用不一致性来消除不正确的模糊解析。 (这两个实现是Elsaour (SD's) C++ Front End。我不能代表Elsa当前的能力(我不认为它已经更新了多年),但我们的所有C ++ 11 [编辑2015年1月:现在完整的C ++ 14编辑2017年10月:现在完整的C ++ 17]包括GCC和微软的变种。)

如果你采用硬计算机科学定义,语言被扩展地定义为一组任意字符串(语法应该是简洁的方法来编码内涵)并将解析视为“检查程序的语法是否正确” “然后对于C ++,您已经扩展了模板,以验证每个模板是否完全展开。有一个图灵机隐藏在模板中,因此在理论上检查C ++程序是否有效是不可能的(没有时间限制)。真正的编译器(尊重标准)对他们将要执行的模板展开有多少限制,实际内存也是如此,因此在实践中C ++编译器完成了。但在这个意义上,他们可以任意长时间地“解析”一个程序。而且我认为这是大多数人关心的答案。

实际上,我猜大多数模板实际上非常简单,因此C ++编译器平均可以像其他编译器一样快速完成。只有疯狂到足以在模板中编写图灵机的人付出了沉重的代价。 (意见:价格实际上是将复杂事物整理到模板上的概念成本,而不是编译器执行成本。)

答案 1 :(得分:1)

很难说C ++是否可以“解析”,因为 - 与大多数语言相反 - 如果不同时执行语义分析,则无法在语法上进行分析。

答案 2 :(得分:1)

取决于“已解析”的含义,但如果您的解析应该包含模板实例化,那么通常不会:

[快捷方式,如果你想避免阅读这个例子 - 模板提供了一个足够丰富的计算模型,实例化它们通常是一个停顿式的问题]

template<int N>
struct Triangle {
    static const int value = Triangle<N-1>::value + N;
};

template<>
struct Triangle<0> {
    static const int value = 0;
};

int main() {
    return Triangle<127>::value;
}

显然,在这种情况下,编译器理论上可以发现三角形数字具有简单的生成器函数,并使用它来计算返回值。但是否则,实例化Triangle<k>将花费O(k)时间,并且显然k可以使用此程序的大小快速上升,直至int的限制类型。

[快捷方式结束]

现在,为了知道Triangle<127>::value是对象还是类型,有效的编译器必须实例化Triangle<127>(同样,在这种情况下,它可能需要一个快捷方式{{1} }被定义为每个模板专门化中的对象,但不是一般的)。符号表示对象或类型是否与C ++的语法相关,所以我可能会认为“解析”C ++ 需要模板实例化。但是,其他定义可能会有所不同。

实际实现任意限制模板实例化的深度,使得大O分析无关紧要,但我忽略了,因为在任何情况下实际实现都有自然资源限制,也使得大O分析无关......

我希望您可以使用递归value在C中生成类似难度的程序,尽管我不确定您是否打算将预处理程序包含在解析步骤中。

除此之外,与许多其他语言一样,C可以有O(不是很多)解析。您可能需要进行符号查找等,正如大卫在他的回答中所说的那样,通常不会有严格的O(1)最坏情况限制,因此O(n)可能有点乐观。即使是汇编程序也可能会查找标签的符号。但是例如动态语言甚至不一定需要符号查找来进行解析,因为这可能是在运行时完成的。如果您选择一种语言,其中所有解析器需要确定哪些符号是关键字,并进行某种括号匹配,那么Shunting Yard算法是O(n),所以有希望。