我经常听到有人说c++是一种上下文敏感的语言。举个例子:
a b(c);
这是变量定义还是函数声明?这取决于符号c的含义。如果c是一个变量,则ab (c);定义一个名为b的类型为a的变量。它直接用c初始化。但如果c是类型,则ab (c);声明一个名为b的函数,该函数接受c并返回a。
如果您查找上下文无关语言的定义,它基本上会告诉您,所有语法规则的左侧必须恰好包含一个非终结符。另一方面,上下文敏感语法允许在左侧使用任意的终结符和非终结符字符串。
浏览“c++程序设计语言”的附录A,我找不到一条语法规则,它的左边除了一个非终结符之外,还有其他任何东西。这意味着c++是上下文无关的。(当然,每一种与上下文无关的语言也是与上下文相关的,因为与上下文无关的语言构成了与上下文相关的语言的一个子集,但这不是重点。)
那么,c++是上下文无关的还是上下文敏感的?
是的,c++是上下文敏感的,非常上下文敏感。您不能通过使用上下文无关的解析器简单地解析文件来构建语法树,因为在某些情况下,您需要从以前的知识中了解符号来决定(例如。在解析时构建一个符号表)。
第一个例子:
A*B;
这是一个乘法表达式吗?
OR
这是B变量作为a类型指针的声明吗?
如果A是一个变量,那么它就是一个表达式,如果A是类型,它就是一个指针声明。
第二个例子:
A B(bar);
这是一个函数原型接受一个条形参数吗?
OR
这是声明A类型的变量B,并调用A的构造函数和bar常量作为初始化式吗?
您需要再次了解bar是符号表中的变量还是类型。
第三个例子:
class Foo
{
public:
void fn(){x*y;}
int x, y;
};
当解析时构建符号表没有帮助时,就是这种情况,因为x和y的声明在函数定义之后。因此,您需要首先浏览类定义,然后在第二步查看方法定义,以确定x*y是一个表达式,而不是指针声明或其他东西。
首先,你正确地观察到c++标准末尾的语法中没有上下文敏感的规则,因此语法与上下文无关。
然而,这种语法并不能精确地描述c++语言,因为它生成的是非c++程序,例如
int m() { m++; }
or
typedef static int int;
c++语言被定义为“一组格式良好的c++程序”,它不是与上下文无关的(可以证明,仅仅要求声明变量就可以做到这一点)。理论上,您可以在模板中编写图灵完备程序,并根据其结果使程序具有病态形式,因此它甚至不是上下文敏感的。
现在,(无知的)人们(通常不是语言理论家,而是解析器设计者)通常在以下一些含义中使用“not context-free”
模棱两可的
不能用Bison解析
而不是LL(k) LR(k) LALR(k)或任何他们选择的解析器定义的语言类
标准后面的语法不满足这些类别(即它是模糊的,不是LL(k)…),所以c++语法对他们来说“不是上下文无关的”。从某种意义上说,他们是对的,要创建一个工作的c++解析器是非常困难的。
注意,这里使用的属性只与上下文无关的语言有微弱的联系——歧义与上下文无关没有任何关系(事实上,上下文敏感的规则通常有助于消除歧义),另外两个只是上下文无关语言的子集。解析与上下文无关的语言不是一个线性过程(尽管解析确定性语言是)。