您可以删除 dangling-else 歧义。在“编译器:原理、技术和工具”第 2 版,第 211-212 页中有这样的语法。但是,这本书有语法,但没有没有解释(或者我没有找到)。
在 ABNF 元语法中,语法是:
statement = matched / open
matched = "if" expr "then" matched "else" matched
/ other
open = "if" expr "then" statement
/ "if" expr "then" matched "else" open
expr = ...
other = ...
它是如何工作的?
这里的想法是通过使用非确定性来避免歧义(无论如何已经提出,因为每个模棱两可的语法也是非确定性的;最后我写了我发现的差异(也是) 经常被误解)。这意味着在成功解析后,任何输入可能只有一个语法树(意味着您要求的非歧义)。但是您需要一个解析器,它与确定性 LL(1) 解析器相比更复杂的机制。
规则matched 确保子树内的每个 直接嵌套的if 语句将具有else。换句话说,then 将有一个匹配的else。
规则open 确保每个子if 语句本身没有else,或者如果有,那么它的else 语句又是open。换句话说,规则open 确保使用less else 关键字多于 then 关键字。换句话说,子树中@987654340@ 的数量将less 比 then 的数量(或者ifs,如果你喜欢) .这与确保两者相等数量的matched 规则形成对比。这意味着在解析器中实现的 matched 和 open 永远不会接受相同的输入,从 first 使用规则 statement 算起。
此外,由于规则open 中的matched "else" open,else 将“附加”到最近的if。这解决了在解析器识别第一个使用的statement之前的悬空-else歧义。
示例
为了说明将要执行的步骤,我升级了语法以允许更“自然”的输入。这意味着更多的标点符号和空格。首先是词法分析器:
keyword = %s"if" / %s"then" / %s"else"
它尽可能将输入字符分组,未分组的字符本身成为标记。输入:
if(x)then if(x)then y else y
你有这些字节:
对于这些字节,您将拥有以下标记:
然后你就有了解析器语法,它允许解析器接受这些标记(区分大小写)并明确放置空格。还是可以看原版的,大体流程是一样的。解析器语法:
statement = matched / open
matched = {keyword, %s"if"} *ws "(" *ws expr *ws ")" *ws {keyword, %s"then"} 1*ws matched {keyword, %s"else"} 1*ws matched
/ other *ws
open = {keyword, %s"if"} *ws "(" *ws expr *ws ")" *ws {keyword, %s"then"} 1*ws statement
/ {keyword, %s"if"} *ws "(" *ws expr *ws ")" *ws {keyword, %s"then"} 1*ws matched {keyword, %s"else"} 1*ws open
expr = %s"x"
other = %s"y"
ws = %s" "
在你开始解析之后,一个深入探索语法规则的解析机,会这样执行:
- 它将进入启动规则
statement
- 然后进入第一个引用规则
matched 的串联。
- 它将接受具有字符
if(x)then 的第一个令牌
- 然后它将再次使用
matched 规则
- 它将接受所有其他令牌,直到输入
if(x)then y else y 结束。
- 然后将移出
matched 规则并期待其匹配的else,但没有这样,因为输入已结束。这意味着解析器必须回去寻找另一种方式。此时,您将拥有这个(部分构造的)语法树:
- 它将回到解析的最开始并尝试规则
statement中的第二个连接,即一个引用规则open。
- 它将按其步骤工作,并最终使用此树成功到达输入的末尾:
- 那么解析机可能会继续探索更多,但不会找到任何其他语法树。
相关理论
确定性、非确定性、模糊性和非模糊性常常被误解。我想分别举一个例子,因为我认为写出来的内容可能对每个人都不是很清楚。
从解析器的角度来看,确定性与给定状态下可能的操作数量有关。当解析器中所有可能的状态大多数动作是可能时,解析器是确定性的。我在很多地方都看到过这样的措辞:“语法主要是确定性的,而在少数地方是非确定性的”。这是误导性的,因为语法的确定性是基于整个语法的——更准确地说,它是最坏的确定性情况。例如,下一个语法是确定性的,当被解析器使用时,解析器也将是确定性的:
rule = "a" / "b"
Non-deterministic 表示解析器存在至少一种可能的状态,其中多于一个 > 对于至少一个输入序列,操作是可能的。例如,下面的语法是不确定的:
rule = "a" / "a"
显然,您可以将其重写为 rule = "a",但这不是相同的语法。两种语法都只生成 same 语言。 语法是生成语言的有效字符串。此语言 由其有效字符串组成。解析器接受有效语言字符串,它拒绝无效语言字符串。
歧义表示输入可以更多而不是一种方式被识别。这反过来意味着对于至少一个有效的输入,多于而不是一个语法树是可能的。例如,前面的语法(rule = "a" / "a")是不明确的,因为两个连接都有a。
Non-ambiguous 意味着任何有效输入都将以完全一种方式被识别。这反过来意味着恰好为每个有效输入存在一个语法树。显然,对于 invalid 输入,有 no 语法树。
您可能有一个非确定性和非模棱两可的语法,例如:
rule = "a" "b" / "a" "c"
对于输入字符a,您在规则的开头有一个不确定的选择,但在下一个输入字符中,哪个连接是正确的。这是不可能,在输入被完全处理后具有 more 多于 一个 语法树:这意味着语法是明确的。
显然,每一个确定性语法也是明确的,因为没有没有状态可能导致第二个动作可能导致第二棵树。
此外,每一个模棱两可的语法都是不确定的,因为为了能够拥有多个语法树,somewhere存在一个状态,它有更多 比 一个 可能的操作,对于至少一个输入序列。
本书语法的“诀窍”是非确定性是让存在(实际上是“雇用”),但它是不让在statement 规则外部传播,以有效防止语法变得不明确。
现在,当解析器一次处理一个字符的输入时,所有这些都是有效的。但我不会离开这里。还有不同类型的歧义。有些你可以计算,有些你不能。但我也不会去那里。
免责声明:我使用了截图和我开发的工具的语法语法:Tunnel Grammar Studio。