以下是我(目前)最喜欢的演示,说明为什么解析C++(可能)是图灵完备的,因为它展示了一个程序,只有给定的整数是质数时,该程序在语法上才是正确的。
因此,我断言C++既不是无上下文也不是有上下文敏感的。
如果您允许在任何产生式的两侧使用任意符号序列,则会生成乔姆斯基层次结构中的类型0语法(“无限制”),这比上下文敏感语法更强大;无限制语法是图灵完备的。上下文敏感(类型1)语法允许在产生式的左侧使用多个上下文符号,但是相同的上下文必须出现在产生式的右侧(因此称为“上下文敏感”)。[1] 上下文敏感语法等效于线性有界图灵机。
在这个示例程序中,素数计算可以由线性有界图灵机执行,因此它并没有完全证明图灵等价性,但重要的是解析器需要执行计算以执行语法分析。它可以是任何可表示为模板实例化的计算,并且有充分的理由相信C++模板实例化是图灵完备的。例如,请参见
Todd L. Veldhuizen's 2003 paper。
无论如何,C++都可以被计算机解析,因此它肯定可以被图灵机解析。因此,无限制文法可以识别它。实际编写这样的文法是不切实际的,这就是标准没有尝试这样做的原因。(请参见下文。)
“歧义”某些表达的问题大多是一个误导。首先,歧义是特定语法的特征,而不是语言。即使可以证明一种语言没有无歧义的语法,如果它可以被上下文无关文法识别,那么它就是上下文无关的。同样地,如果它不能被上下文无关文法识别,但可以被上下文有关文法识别,那么它就是上下文有关的。歧义是不相关的。
但无论如何,就像下面程序中的第21行(即
auto b = foo<IsPrime<234799>>::typen<1>();
)一样,这些表达式根本不会产生歧义;它们只是根据上下文以不同的方式解析。在问题的最简表达式中,某些标识符的语法类别取决于它们的声明方式(例如类型和函数),这意味着形式语言必须认识到同一程序中两个任意长度的字符串是相同的(声明和使用)。这可以通过“复制”语法来建模,该语法识别两个连续的完全相同的单词。可以用
pumping lemma轻松证明这种语言不是上下文无关的。这种语言的上下文有关语法是可能的,并且在这个问题的答案中提供了一个0型语法:
https://math.stackexchange.com/questions/163830/context-sensitive-grammar-for-the-copy-language。
如果有人试图编写一个上下文敏感(或无限制)的语法来解析C ++,那么它很可能会用涂鸦填满整个宇宙。编写一个图灵机来解析C ++同样是不可能的任务。即使编写C ++程序也很困难,据我所知,没有一个被证明是正确的。这就是为什么标准不尝试提供完整的形式语法定义,并且选择用技术英语编写一些解析规则的原因。
在C ++标准中看起来像形式语法的东西并不是C ++语言语法的完整形式定义。甚至不是预处理后语言的完整形式定义,这可能更容易形式化。 (然而那不是语言:由标准定义的C ++语言包括预处理器,并且预处理器的操作是通过算法描述的,因为在任何语法形式中描述它都非常困难。在标准的这一部分中描述了词汇分解,包括必须多次应用的规则。)
各种语法(两个重叠的词法分析语法,一个在预处理之前进行,另一个在必要时之后进行,以及“句法”语法)收集在附录A中,并附有重要说明(强调添加):
这份C++语法摘要旨在帮助理解,但
它并不是该语言的精确陈述。特别地,此处描述的语法接受
有效C++构造的超集。必须应用消歧规则(6.8、7.1、10.2)来区分表达式和声明。此外,必须使用访问控制、歧义和类型规则来淘汰语法上有效但无意义的结构。
最后,这是承诺的程序。如果且仅当IsPrime<N>
中的N为质数时,第21行才是语法正确的。否则,typen
是一个整数,而不是一个模板,因此typen<1>()
被解析为(typen<1)>()
,这是语法上不正确的,因为()
不是一个语法上有效的表达式。
template<bool V> struct answer bool operator()()};
template<bool no, bool yes, int f, int p> struct IsPrimeHelper
: IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> ;
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> ;
template<int f, int p> struct IsPrimeHelper<false, true, f, p> ;
template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X ;
template<typename A> struct foo;
template<>struct foo<answer<true>>;
template<> struct foo<answer<false>>;
int main()
[1] 更准确地说,上下文敏感语法中的每个产生式必须具有以下形式:
αAβ → αγβ
其中A
是非终结符,α
、β
是可能为空的语法符号序列,γ
是非空序列。(语法符号可以是终结符或非终结符)。
这可以被理解为只有在上下文[α,β]
中才能将A → γ
。在上下文无关(第2型)语法中,α
和β
必须为空。
事实证明,您还可以通过“单调”限制来限制语法,其中每个产生式必须具有以下形式:
α → β
,其中|α| ≥ |β| > 0
(|α|
表示“α
的长度”)
可以证明,由单调文法识别的语言集合与由上下文敏感文法识别的语言集合完全相同。通常情况下,基于单调文法进行证明更容易,因此经常会看到人们将“上下文敏感”用作“单调”的意思。