上下文敏感性与歧义

Context-sensitivity vs Ambiguity

本文关键字:歧义 敏感性 上下文      更新时间:2023-10-16

我对上下文敏感性和歧义如何相互影响感到困惑。

我认为正确的是:

多义性:

一个模棱两可的语法会导致使用左派生或右派生构造多个解析树。所有可能的语法都模棱两可的语言是模棱两可的语言。

例如C++是一种模棱两可的语言,因为 x * y 总是意味着两种不同的东西,如中所述:为什么不能用 LR(1) 解析器解析C++?

上下文敏感度:

上下文相关语法具有规则,其中这些规则的左侧可能包含(非)终端符号,除了不同类型语法的所有规则的 lhs 中所需的一个非终端符号之外。这意味着您不能在下降时仅更换非终端。相反,您必须首先查看周围的非终端。


现在困扰我的是或多或少说上下文相关解析器可以解析像 x * y 这样的歧义的陈述。例如,在上述链接问题中指出"......一个解析器[在创建语法树时装饰它]不是上下文无关的,而LR解析器(纯解析器)是上下文无关的。在我看来,这意味着上下文相关的解析器(与上下文无关的解析器相反?)可以做到这一点。另一个例子是C++语法上下文的任何部分是否敏感?这个问题的回答是"是..."。同样在这里:什么是模棱两可的上下文自由语法?

我不明白这种C++歧义与上下文敏感性有什么关系。我不认为有任何上下文相关的语法可以处理这种歧义。例如,如果您采用虚构的规则,例如Typedef, *, PointerDeclaration -> Ident "*" Ident

那么你仍然无法确定(通过纯解析)在Typedef期间是否使用了具体的第一个Ident(例如"x")(例如typedef双x;)。


因此,术语

"上下文敏感"是否有可能在链接的问题中使用,尽管这意味着像上下文依赖性这样简单的东西(例如,需要比简单解析器提供的信息更多的信息)。或者"真正的"上下文敏感性"和歧义之间是否存在任何联系。

编辑 更具体的问题:上下文无关语法中是否存在任何可以通过使用上下文相关语法来处理的歧义。我之所以想到这个问题,是因为在链接的问题中,听起来C++歧义有时被称为上下文敏感问题。

Edit2 附加信息:《计算机系统》一书在第 346 页指出,诸如具有相同数量的实际和形式参数之类的要求可以通过上下文相关语法来表达。但这非常麻烦,因为您需要很多复杂的规则。但也许这也适用于前面提到的C++歧义。这样我们就有了这样的规则

"Typedef double x", *, 指针声明 -> "x" "*" 标识

当然,这样的规则将非常有限,你需要大量的时间来表达每一种可能性。如果(理论上)上下文无关的歧义可以用上下文相关规则的使用代替,至少这可能是回答问题的一种方法

上下文敏感度和歧义是完全正交的。存在不明确的上下文无关语言和明确的上下文相关语言。

上下文相关语言

是一种形式语言,可以通过上下文相关语法 (CSG) 进行分析。每种上下文无关语言也是上下文相关语言,因为上下文无关语法是简化的上下文相关语言。不过,并非每种正式语言都与上下文相关;有些语言即使是 CSG 也无法描述。

如果要

使用上下文无关分析器分析上下文相关语言,请定义一个上下文无关语法,该语法接受上下文相关语言的超集(因为它们功能较弱)。由于您接受超集,因此可能会得到歧义或误报结果,必须在分析后解决。

示例一:一种类似 XML 的语言,允许任何标记名称。由于上下文无关语法无法解析由两个重复单词 w = {a,b}+ 组成的句子 ww,因此它无法解析 ID 相等<ID></ID>。因此,可以定义一个接受超集的上下文无关语法:

start -> elem
elem  -> open elem* close
open  -> '<' ID '>'
close -> '</' ID '>'
ID    -> ('a' / 'b')+

这显然甚至解析了人们不想要的句子,因此必须对openclose中的等效 ID 进行额外检查。

示例二:一种非常简单的语言中的类似C的Typedef。想象一种只包含 typedef、指针和乘法的语言。它只有两个ID,ab。这种语言的示例:

typedef a;
b * a;                    // multiplication
a * b;                    // b is pointer to type a 

上下文无关的语法如下所示:

start                     -> typedef multiplication-or-pointer+
typedef                   -> 'typedef' ID ';'
multiplication-or-pointer -> ID '*' ID ';'
ID                        -> 'a'
ID                        -> 'b'

语法不接受超集,但它不知道它是否看到乘法或指针,因此它是模棱两可的。因此,必须检查结果并决定它是乘法还是指针,具体取决于 typedef 中定义的类型。

使用上下文相关的语法,人们可以做更多的事情。非常粗略(且不精确):

start                                     -> typedef multiplication-or-pointer+
typedef                                   -> 'typedef' ID ';'
multiplication-or-pointer                 -> pointer / multiplication
'typedef' 'a' ';' WHATEVER pointer        -> 'a' '*' ID   
'typedef' 'b' ';' WHATEVER pointer        -> 'b' '*' ID   
'typedef' 'b' ';' WHATEVER multiplication -> 'a' '*' ID
'typedef' 'a' ';' WHATEVER multiplication -> 'b' '*' ID
ID                                        -> 'a'
ID                                        -> 'b'

请注意,我在这里显示的内容并不准确,因为我限制了 ID 的数量。通常,可以有无限数量的 ID。您可以为一般情况编写上下文相关语法(即使它一定是绝对不直观的),但不能编写上下文无关的语法。

关于您的编辑 1:我希望前面的示例可以回答这个问题。

关于您的编辑 2:还有另一种技巧如何表达,因此规则不受限制,但它们通常令人兴奋,IMO 这就是没有人使用 CSG 形式主义的原因。

注意:上下文相关语法等效于

线性有界自动机,上下文无关语法等效于下推自动机。说上下文无关解析器与上下文相关解析器相反是不对的。

编译器不使用"纯"(无论这意味着什么)语法来进行解析 - 它们是执行所有真实世界程序所做的事情的真实程序 - 在某些情况下应用启发式方法。这就是为什么C++编译器(以及大多数其他语言的编译器,本科练习除外)不使用编译器生成器生成的原因。