有人可以用简单的术语解释一下递归下降解析器是什么吗?
我被困在试图得到它。在wikipedia中解释的确非常模糊。
Recursive Descent Parser是一种自上而下的解析器,它构建为一组递归过程,每个过程都实现了语法的生成规则。
那么,我能做对吗?解析器是一个程序,它以预定义的顺序逐个执行命令,每次执行时每个命令具有相同的含义,但它根据输入以某种方式调整输出,这意味着每次输入时调整都可能不同改变了。
我仍然不明白为什么在这里使用递归这个词。
答案 0 :(得分:11)
首先,一堆术语。
解析器是可以根据某种语法检查文本输入在语法上是否正确的软件。解析器还可能将文本输入转换为另一种表示,以便其他软件使用。
语法是语言语法的定义。 语言是所有语法正确的句子(可能是无限的)。"句子。句子是一系列符号。
用一组制作描述语法。 制作是规则,告诉您如何用其他符号序列替换符号序列
现在我们可以用一个例子来说明这一点:具有平衡括号的所有可能序列的简单语言。例如,字符串"()"将是语言的成员,"()()()"和"((()))"。我们的语言不会包含一系列不平衡的括号:"("和"())"不属于我们的语言。
这种语言的语法可以这样写:
S ::= ""
S ::= '(' S ')' S
这里,S
是非终端符号,具体而言,是起始符号。每行代表语法中的一个产生。更有趣的语言有更多的非终端符号和更多的制作。
如果要为我们的语言生成有效的字符串,请从字符串S
开始,然后迭代地应用生产规则,用新序列替换字符串中的所有非终端符号。
所以我们从S
开始,然后选择一个生产规则来应用。假设我们选择第二个,我们得到( S ) S
。由于我们仍然在字符串中有非终端,我们必须继续前进。如果我们再次使用第二个规则替换第一个S
,我们会得到( ( S ) S ) S
。现在让我们开始选择第一条规则,即我们可以用空字符串替换S
。 (我将其写为""
,但有时您会看到人们使用希腊语epsilon。)如果我们将此规则应用于字符串中所有剩余的S
es,我们最终会使用( ( ) )
,这是该语言中的有效序列。
好的,但现在我们想要走另一条路。我们得到一个字符串作为输入,并想知道它是否属于该语言。这是解析器的工作。
对于许多(但不是全部)语法,您可以使用称为递归下降解析器的特定样式的解析器实现。基本思想是编写与语法中的作品相对应的函数。每个函数都可以调用其他函数来检查子字符串。他们甚至可以自称(这是"递归"发挥作用的地方)。
让我们稍微改写一下我们的语法:
S ::= '(' P | ""
P ::= S ')' S
垂直条表示"或"。因此S
可由( P
或替换为空字符串。
现在假设我们编写了两个名为ParseS
和ParseP
的函数。这些函数可以看到输入字符串的其余部分,如果字符串的下一位与相应的生成匹配,则返回true。在伪代码中:
bool ParseS() {
if next character is '(' {
skip the `(`
return ParseP()
}
return true; // handles the empty string
}
bool ParseP() {
if ParseS() and the next character is `)` {
skip the ')'
return ParseS();
}
return false;
}
这些函数一起形成了我们语言的递归下降解析器。[*]它们告诉我们输入字符串是否是语法定义的语言,这是解析器的基本定义。它是递归的,因为ParseS
可以调用可以调用ParseP
的{{1}}。
[*]好吧,差不多。它实际上有点过于简单了。正确的解析器会检查以确保在返回最终的true之前不再有输入。如上所述,这个将接受任何前缀是该语言成员的字符串。这在实践中很容易解决,但它会在已经太长的答案中引入令人分心的细节。