PLY 的 parsetab.py 是如何格式化的?

How is PLY's parsetab.py formatted?

我正在从事一个将 MATLAB 代码转换为 Python 的项目,并且在构建 others work. The tool uses PLY 之后取得了一定的成功([=41= 的 lex 和 yacc 解析工具的实现) ]) 解析 MATLAB 输入。不幸的是,要求我的代码是用 Python 3 编写的,而不是 Python 2。该工具在 Python 2 中运行没有问题,但我在 [=41] 中得到一个奇怪的错误=] 3(假设A是一个数组):

    log_idx = A <= 16;
                  ^
SyntaxError: Unexpected "=" (parser)

我尝试转换的 MATLAB 代码是:

idx = A <= 16;

在 Python 3 中应该转换为几乎相同的东西:

idx = A <= 16

Python 3 代码和 Python 2 代码之间唯一真正的区别是 PLY 生成的 parsetab.py 文件,它在以下变量中有实质性差异:

_tabversion
_lr_signature
_lr_action_items
_lr_goto_items

我无法理解这些变量的用途以及为什么它们可能不同,而唯一的区别是用于生成 parsetab.py 文件的 Python 版本。

我尝试搜索这方面的文档,但没有成功。我最初怀疑 Python 2 和 Python 3 之间的字符串格式化方式可能有所不同,但这也没有任何结果。有没有熟悉 PLY 的人可以深入了解这些变量是如何生成的,或者为什么 Python 版本会造成这种差异?

编辑:我不确定这是否对任何人有用,因为该文件很长而且很神秘,但下面是 _lr_action_items 和 [=20= 的第一行的一部分示例]

Python 2:

_lr_action_items = {'DOTDIV':([6,9,14,20,22,24,32,34,36,42,46,47,52,54,56,57,60,71,72,73,74,75 ...
_lr_goto_items = {'lambda_args':([45,80,238,],[99,161,263,]),'unwind':([1,8,28,77,87,160,168,177 ...

Python 3:

_lr_action_items = {'END_STMT':([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,39,41,48,50 ...
_lr_goto_items = {'top':([0,],[1,]),'stmt':([1,44,46,134,137,207,212,214,215,244,245,250 ...

我要在这里冒险,因为您几乎没有提供任何关于您实际使用的代码的指示。所以我假设您从您在问题中链接到的 github 存储库复制了 lexer.py file

此错误消息中有一个重要线索:

log_idx = A <= 16;
              ^
SyntaxError: Unexpected "=" (parser)

显然,<= 作为单个标记进行扫描;否则,解析器将不会在输入的那个点看到 = 标记。这只能意味着扫描器正在返回两个标记,<=,如果是这种情况,那肯定是语法错误,正如您从

中所期望的那样
log_idx = A < = 16;

要弄清楚为什么词法分析器会这样做,了解 Ply(默认)词法分析器的工作原理很重要。它从名称以 t_ 开头的变量收集所有词法分析器模式,这些变量必须是函数或值为字符串的变量。然后按如下方式对它们进行排序:

  1. 函数文档字符串,按源文件中的行号排序。
  2. 字符串值,按长度倒序排列。

参见 Ply 手册中的 Specification of Tokens

这通常是正确的,但并非总是如此。按长度倒序排序的目的是前缀模式将出现在匹配较长字符串的模式之后。因此,如果您有模式 '<''<=',将首先尝试 '<=',因此在输入有 <= 的情况下,< 模式将永远不会被审判。这很重要,因为如果先尝试 '<''<=' 将永远不会被识别。

但是,这种简单的启发式方法并不总是有效。正则表达式较短的事实并不一定意味着它的匹配项会较短。所以如果你期望 "maximal munch" 语义,你有时必须小心你的模式。 (或者您可以将它们作为文档字符串提供,因为这样您就可以完全控制订单。)

创建那个 lexer.py 文件的人 关注他们的模式,因为它包括(除其他问题外):

t_LE          = r"<="
t_LT          = r"\<"

请注意,由于这些是原始字符串,反斜杠保留在第二个字符串中,因此两个模式的长度均为 2:

>>> len(r"\<")
2
>>> len(r"<=")
2

由于两个模式的长度相同,因此它们在排序中的相对顺序是未指定。 Python 的两个版本很可能会产生不同的排序顺序,这要么是因为 sort 的实现方式不同,要么是因为变量字典迭代的顺序不同,或者某些以上的组合。

<在Python正则表达式中没有特殊意义,所以在t_LT的定义中不需要反斜线转义。 (显然,因为它在 t_LE 中没有反斜杠转义。)所以最简单的解决方案是通过删除反斜杠使排序顺序明确:

t_LE          = r"<="
t_LT          = r"<"

现在,t_LE更长,一定会先试的

这不是词法分析器文件中此问题的唯一实例,因此您可能需要仔细修改它。

注意:您还可以通过向 t_LE 模式添加不必要的反斜杠来解决此问题;采取这种态度是有争议的,"When in doubt, escape." 但是,了解 Python 正则表达式中哪些字符需要转义以及 re 的 Python 文档很有用] 包中包含完整列表。此外,考虑对包含引号的模式使用长原始字符串,因为 "' 都不需要在 Python 正则表达式中转义反斜杠。