令牌已被另一条规则使用,无法再次解析令牌

The token was already consumed by another rule, Can't parse the token again

我正在构建一个 LL(1) 解析器(递归下降解析器),我需要解析句子 a = 3,我有两个过程来匹配该规则:parse_assignmentparse_binary_operator,

每个函数消耗第一个token,然后它需要查看运算符(=)是否匹配规则,所以我首先调用parse_assignment消耗token“a”,然后然后消费“=”,然后消费“3”。如果函数失败(不匹配),我会尝试匹配下一条规则。

但是如果我想解析句子a - 1怎么办? 我尝试调用 parse_assignment 但随后它消耗了 a 并失败了,然后我尝试调用 parse_binary_operator 但令牌“a”已经被消耗所以该函数只有“[=19” =]" 现在解析。

所以我需要以某种方式 return 将令牌发送到流以防函数不匹配,但我认为 return 将令牌返回流不是一件好事想法,我想有更稳定的技术可以做到这一点。

“LL(1)”中的“(1)”表示允许解析器参考 (1) 未使用的令牌,以便决定采取哪个操作。此标记通常称为“先行”标记,因为解析器可以先行查看它而无需实际使用它。无论您在解析器和词法分析器之间实现什么接口,都必须以某种方式实现此功能;否则,您将 运行 准确地解决您提到的问题。

此接口的确切性质将取决于您使用哪种语言编写解析器,因此很难给出具体的答案。但基本上,词法分析器将有两个基本方法:

  • peek,其中 return 是先行标记的标记类型。 (令牌通常具有许多附加功能,所有这些功能都应该以某种方式通过 peek 或其他方法提供,这些方法也是先行令牌的视图。这些包括令牌的语义值,如果有的话,以及令牌在源文件中的位置的一些指示。
  • consume,它会丢弃先行标记并将其替换为下一个输入标记。

还有其他可能的接口,它们本质上是等价的。例如,实现布尔便利函数(如“匹配 X 并在成功时使用令牌”)是很常见的,尽管如果您还需要 return 匹配令牌的语义值,这可能不会那么方便。

先行标记是词法分析器状态的一部分,其中还包括诸如当前正在分析的字符串和该字符串中的当前输入位置等信息。将此状态存储在全局变量中曾经很常见(这是在 (f)lex 的帮助下构建的词法分析器所做的),但这使得使用两个不同的词法分析器编写程序变得更加困难。现代编程风格不鼓励使用全局变量,这使得必须在解析器状态(或对象)中包含词法分析器的状态(或对象),或者通过递归解析调用显式传递对词法分析器状态的引用。