首页 > 解决方案 > Python PLY 在解析期间获取下一个令牌

问题描述

我正在尝试获取下一个令牌并根据它进行一些操作。我知道这很奇怪,但仍然有可能做到这样的事情吗?:

def p_func(p):
    '''expr : MY_TOKEN'''
    if next_token is None:
        #do something here
    p[0] = p[1]

我尝试执行以下操作:

def p_func(p):
    '''expr : MY_TOKEN'''
    if parser.token() is None:
        #do something here
    p[0] = p[1]

它可以获取令牌,但是在此函数之后,下一个令牌跳过了,因为我拿了它。是否可以将其退回或仅获取下一个令牌的副本?

标签: pythoncontext-free-grammarply

解决方案


我不相信在 Ply 中有可靠的方法来做到这一点。

Ply 通常在执行归约之前读取下一个标记(“前瞻标记”),因此调用parser.token()通常会返回第二个下一个标记。但是 Ply 不保证在归约之前读取下一个标记:在某些情况下,它可以在没有前瞻的情况下推断出动作,它会立即执行归约。因此,由 产生的令牌parser.token()可能是下一个令牌,因为它显然在您尝试它的特定规则中。

如果您需要一致性,您可以指示 Ply 在进行归约之前始终读取前瞻标记。显然,没有办法告诉它永远不要读取前瞻标记,因为有时需要决定解析器的操作。

如果前瞻标记可用于解析器操作,这将很好,因为它在由野牛生成的解析器中(例如)。不幸的是,在 Ply 中,前瞻标记作为局部变量保存在解析器中,这样更有效但更难访问。

您可以修改 Ply 的源代码,使当前的前瞻标记成为解析器对象的成员,而不是局部变量。(lookahead如果你想追求这个想法[注 1],就会调用它。)这会在解析器中引入一个非常小的减速,但我怀疑它在实践中是否可见。但是,这会使共享代码变得更加复杂;您必须将整个修改后的 Ply 包(可能已重命名)作为应用程序的一部分分发。

前瞻令牌最常见的用途是制作更好的错误消息,或以其他方式协助错误恢复。使用它来改变归约动作的行为让我觉得不是最理想的,因为大多数这样的情况都可以通过使用更好的语法来实现。但大概你已经探索了替代方案,所以我将把它留在那里。


笔记

  1. 修改时要小心yacc.py:Parser 对象有多个版本,基于不同的可能优化。标准安装脚本从骨架自动生成此文件,以保持各种优化的实现彼此同步。要进行此修改,您要么必须使用 Ply 的构建机制,要么在所有不同版本中仔细进行更改(并且有评论建议您不要这样做。)

推荐阅读