递归下降分析器

2025年4月2日 | 阅读 8 分钟

自顶向下解析器被称为递归下降解析器,它使用递归方法根据语法规则分析输入。 解析器从语法的最顶层规则开始,对每个不是终结符的输入符号向后执行子例程,直到它到达一个。 解析器的输出是一个解析树,它根据语法表示输入的结构。

递归下降解析

解析过程经常用于语言处理和编译器设计。 它基于可以将一个复杂问题分解成更简单的问题,然后递归地解决的想法。 解析过程从一个顶层非终结符开始,通过递归地扩展非终结符直到它到达终结符来继续。

重复下降,解析背后的基本思想是创建一组解析函数,每个函数对应于一个非终结语法符号。 这些过程负责定位和评估相关的语言组成部分。 解析器首先调用顶层函数,然后根据输入的结构递归地调用必要的解析例程。

Recursive Descent Parser

语法

开发递归下降解析器的第一步是定义要解析的语言的语法。 称为语法的规则集合定义了语言的语法。 每个规则由右侧的一系列终结符和非终结符以及左侧的一个非终结符组成。

以用于一个简单的算术语言的语法为例

表达式、项、因子和数字是语法的四个非终结符。 表达式规则定义一个算术表达式为一个或多个项,这些项由加号或减号分隔,它作为解析的基础。 根据项规则,一项定义为一个或多个因子,这些因子由乘号或除号分隔。 因子规则指的是一个数或一个用括号括起来的表达式。 根据数字规则,一个数由一个或多个数字组成。

解析

递归下降的基本原则是编写一组递归函数,每个函数对应于语法中的一个非终结符,这就是解析过程。 每个函数被分配给一个语法规则,它必须解析与特定规则匹配的一系列符号。

表达式函数从递归下降解析器开始,该函数用输入字符串调用。 根据该符号是数字还是左括号,该函数分析输入的第一个符号并选择要应用的术语规则的哪种替代方案。 如果它是一个数字,则使用因子函数来解析该符号的值。 如果该符号是一个左括号,则使用表达式函数递归地解析括号内的表达式。 在因子或表达式函数返回后,将递归调用项函数来解析任何后续的乘号或除号和因子。

如果项函数返回一个值,则表达式函数确定项之后是否有任何加号或减号。 如果是,则再次调用项函数以解析后续项。 在发生错误或所有输入都已解析之前,此过程将继续。

例如,假设我们要解析公式 2 + 3 * (4 - 1) / 2。 这就是递归下降解析器的操作方式

解析器首先使用提供的字符串调用表达式函数。 当提供“2”作为输入时,该函数调用另一个函数,该函数随后执行数据并返回。 然后,表达式函数读取下一个符号,一个加号。 它再次使用输入“,”调用项函数。

递归下降解析具有以下优点

  1. 易于使用:因为递归下降解析器紧密模仿了被解析语言的语法规则,所以它很容易理解和使用。
  2. 可读性:解析代码通常以结构化和模块化的方式设置,这使得它更易于阅读和维护。
  3. 递归下降解析器可以生成描述性错误消息,这使得更容易在输入中查找和检测语法错误。 3. 错误报告。
  4. 可预测性:递归下降解析器的可预测行为使解析过程具有确定性和清晰性。

然而,递归下降解析也有一些缺点

  1. 递归下降解析器在处理左递归语法规则时会遇到困难,因为它们可能导致无界递归。 为了有效地处理左递归,必须小心避免它或使用诸如记忆化之类的方法。
  2. 当语法规则的内部替代方案不成功时,递归下降解析器依赖回溯。 这可能导致效率低下,尤其是在语法包含大量歧义或选项的情况下。
  3. 递归下降解析器通常遵循 LL(1) 约束,这要求它们仅使用一个前瞻标记。 这种限制约束了语法的表现力,因为它无法处理一些模棱两可的或上下文相关的语言。

下面提供了递归下降解析算法的概要

  • 语法:解析语言的第一步是定义其语法。 一组生产规则构成了语法,这些规则概述了语言的句法结构。 每个规则由右侧的一系列终结符和非终结符以及左侧的一个非终结符组成。
  • 创建解析函数:为语法中的每个非终结符创建一个解析函数。 识别和解析与每个非终结符对应的语言表达式的任务将落入每个函数。
  • 输入标记读取:读取来自词法分析器或词法分析器的输入标记。 这些标记代表输入语言的 ID、关键字、运算符和其他组件。
  • 实现解析函数:递归地实现每个解析函数。 每个函数应遵循以下步骤
    1. 验证当前标记是否与非终结符的预期符号匹配。
    2. 如果非终结符有多个生产规则,请使用 if-else 或 switch 语句处理每个替代方案。 每一个可能性都应该由一个不同的函数调用或代码块来表示。
    3. 递归地调用规则中每个替代方案的匹配非终结符的解析例程。 由于此递归调用,解析过程将继续进行,直到所有输入都已被处理。
    4. 处理任何额外的非终结符特定逻辑,例如解析树构造或语义操作。
  • 开始解析:通过调用对应于语法的起始符号的解析函数来启动解析操作。 递归下降解析过程将从该函数开始。
  • 实现错误处理程序以处理异常输入或通知语法错误。 当发生一个错误时,给用户清晰的错误消息,以便他们可以理解并修复该问题。

通过执行以下列出的步骤,可以使用递归下降解析算法来解析给定语言的语法规则。

代码实现

输出

Recursive Descent Parser

给定的代码实现了一个简单的计算器应用程序,用于评估算术表达式。

  • 该代码定义了几种标记类型,例如 INTEGER、PLUS、MINUS、MULTIPLY、DIVIDE、LPAREN、RPAREN 和 EOF。 这些标记类型充当输入表达式的各种元素的表示。
  • 标记由称为 Token 类的简单数据结构表示。 它包含两个属性:value 记录相关值,type 保存标记类型。
  • 输入文本由 Lexer 类标记化。 逐字符读取文本,定位标记,并将它们发回给解析器。
  • advance() 方法将输入文本推进到下一个字符。
  • 使用输入文本,integer() 方法提取一系列数字并返回匹配的整数值。
  • 词法分析器的主要功能是 get_next_token() 方法。 它查看文本以查找当前标记,然后再返回 Token 对象。
  • main() 方法用作程序的起点。
  • 它从用户那里请求一个等式,然后读取输入。
  • 从输入文本创建一个 Lexer 对象,并从 Lexer 创建一个 Parser 对象。
  • 它调用解析器的 parse() 方法来解析表达式并确定结果。
  • 之后,打印结果。

结论

递归下降解析是编译器设计和解析理论中的一个关键方法。 由于它提供了一种简单直观的解析方式,因此是简单语言和教育原因的热门选择。 任何对语言解析和编译器开发感兴趣的人都将受益于理解递归下降解析器的基本原理和实际应用。