Parsing 递归下降解析器易于解释

Parsing 递归下降解析器易于解释,parsing,recursion,recursive-descent,Parsing,Recursion,Recursive Descent,有人能简单地解释一下什么是递归下降解析器吗 我一直想得到它。这是非常模糊的解释 递归下降解析器是一种自上而下的解析器,它由一组递归过程构成,每个递归过程实现语法的产生式规则 那么,我理解对了吗?解析器是一个按预定义顺序逐个执行命令的程序,每次执行命令时,每个命令都具有相同的含义,但它会根据输入以某种方式调整输出,这意味着每次更改输入时,调整可能会有所不同 我仍然不明白为什么这里使用递归这个词。首先,一系列术语 解析器是一种软件,它可以根据某些语法检查文本输入的语法是否正确。解析器还可以将文本输入

有人能简单地解释一下什么是递归下降解析器吗

我一直想得到它。这是非常模糊的解释

递归下降解析器是一种自上而下的解析器,它由一组递归过程构成,每个递归过程实现语法的产生式规则

那么,我理解对了吗?解析器是一个按预定义顺序逐个执行命令的程序,每次执行命令时,每个命令都具有相同的含义,但它会根据输入以某种方式调整输出,这意味着每次更改输入时,调整可能会有所不同


我仍然不明白为什么这里使用递归这个词。

首先,一系列术语

解析器是一种软件,它可以根据某些语法检查文本输入的语法是否正确。解析器还可以将文本输入转换为其他软件更容易使用的另一种表示形式

语法是一种语言语法的定义。语言是所有语法正确的“句子”的集合(可能是无限的)。句子是一系列符号

语法是用一组结果来描述的产品是告诉您如何用其他符号序列替换中的符号序列的规则

现在我们可以用一个例子来说明这一点:一种包含所有可能的平衡圆括号序列的简单语言。例如,字符串“()”将是该语言的成员,“()”和“(())”也是该语言的成员。我们的语言不会包含不平衡的括号字符串:“((“())”不是我们语言的一部分

这种语言的语法可以这样写:

S ::= ""
S ::= '(' S ')' S
这里,
S
是一个非终端符号,特别是开始符号。每一行代表语法中的一个结果。更有趣的语言有更多的非终端符号和更多的产品

如果您想为我们的语言生成一个有效的字符串,可以从字符串
S
开始,然后迭代地应用产生式规则,用新序列替换字符串中的任何非终端符号

因此,我们从
S
开始,选择一个要应用的产生式规则。假设我们选择第二个,我们得到
(S)S
。因为字符串中仍然有非终端,所以我们必须继续。如果我们再次用第二个规则替换第一个
S
,我们得到
((S)S)S
。现在让我们开始选择第一条规则,它说我们可以用空字符串替换
S
。(我写的是
,但有时你会看到人们用希腊字母epsilon来表示。)如果我们将此规则应用于字符串中所有剩余的
S
e,我们将得到
(())
,这是该语言中的一个有效序列

好的,但是现在我们想走另一条路。我们得到一个字符串作为输入,并想知道它是否属于该语言。这是解析器的工作

对于许多(但不是所有)语法,您可以使用一种特殊的解析器实现样式,称为递归下降解析器。基本思想是编写与语法中的结果相对应的函数。每个函数都可以调用其他函数来检查子字符串。他们甚至可以自称(这就是“递归”发挥作用的地方)

让我们换一种方式来写语法:

S ::= '(' P | ""
P ::= S ')' S
竖条的意思是“或”。因此,可以用
(P
或空字符串替换
S

现在假设我们编写了两个函数,分别称为
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
,它可以调用
ParseS


[*]几乎是这样。它实际上有点过于简单化了。正确的解析器会在返回最终的true之前检查以确保没有更多的输入。如前所述,此解析器将接受前缀为该语言成员的任何字符串。这在实践中很容易修复,但会在已经太长的回答。

精彩的解释!我把一切都准备好了!非常感谢您的关注!比return true/false更好的是return。例如,return Result::unmatchedparenthes,其中Result是一个枚举。这使您的代码更加灵活易读。@AleksandrH:经典的解析器只检查输入是否正确是否是语言的一部分,因此布尔结果是传统的。在生产代码中(与说明性伪代码相反),是的,您希望能够报告更详细的错误信息。样式各不相同,但额外的信息通常存储在一些辅助数据结构中,因为true/false返回使得语法产品和函数之间的映射非常直接,这是使用递归下降解析器的主要原因。