如何防止在PLY中重新生成表格

9

我在一个命令行应用程序中使用PLY,将其打包为Python egg以通过pip进行安装。每次我从命令行运行脚本时,都会看到以下信息:

"Generating LALR tables"

此外,解析器输出文件parser.out和parsetab.py文件将被写入调用脚本的目录。有没有办法将这些文件随应用程序一起打包,以便它不必每次都重新生成表格?


我不知道是否可以使用ply来完成这个任务。您可以更改要使用的文件名或目录,或选择始终重新生成表格(这可能取决于语法的大小)。但是要生成解析器表格以进行发布...我不确定。 :/ - Elias Dorneles
5个回答

12
yacc.yacc(debug=0, write_tables=0)

1
注意:你应该使用 debug=False, write_tables=False。虽然 0 可以工作,但使用 True/False 更明确,文档也使用布尔值而不是整数。 - Bakuriu

3
你想要使用优化模式,调用lex如下:
lexer = lex.lex(optimize=1)

值得强调的是(来自同一链接):

在后续执行中,lextab.py将被简单地导入以构建词法分析器。这种方法极大地提高了词法分析器的启动时间,并且它在Python的优化模式下工作。

当运行在优化模式下时,需要注意的是lex禁用了大多数错误检查。因此,只有当您确定一切正常并准备开始发布生产代码时,才推荐使用这种方法

由于这是生产代码,这听起来正是您想要的。

.

在研究此问题时,我发现杂项Yacc笔记

由于LALR表的生成相对昂贵,如果可能的话,先前生成的表将被缓存和重复使用。重新生成表的决定是通过取所有语法规则和优先级规则的MD5校验和来确定的。只有在不匹配的情况下,表格才会重新生成。

深入研究yacc.py中的yacc函数,我们发现优化在以下代码片段中忽略了此不匹配:

if optimize or (read_signature == signature):
    try:
        lr.bind_callables(pinfo.pdict)
        parser = LRParser(lr,pinfo.error_func)
        parse = parser.parse
        return parser

其中signature与存储在parsetab.py中的校验和进行比较(作为_lr_signature)。


除了在解析器和词法分析器中设置 optimize=1,还有其他需要做的吗?当我这样做时,它仍会重新生成文件并仍会打印出消息。 - Michael

0

这是一个老问题,但我在使用 outputdir yacc 关键字参数将生成的解析器表放置在项目特定目录中时遇到了与 ply 相似的问题。它会将它们放在那里,但每次重新生成它们。我在 Github 上找到了 this 补丁,它解决了再生成的问题,没有明显的不良影响。基本上,它只是修改了 yacc 类上的 read_table 方法以接受一个额外的参数 -- outputdir -- 并在重新生成之前在该目录中查找。为了使其工作,yacc 方法中对 read_table 的唯一调用站点还需要修改以传递 outputdir 关键字参数。


0
我最终做的是关闭优化。我正在查看PLY 3.4源代码,我在词法分析器代码中发现了这个小宝石:
# If in optimize mode, we write the lextab
if lextab and optimize:
    lexobj.writetab(lextab,outputdir)

return lexobj

通过更改构建词法分析器和语法分析器的代码为: self.lexer = lex.lex(module=self, optimize=False, debug=False, **kwargs)self.lexer = lex.lex(module=self, optimize=False, debug=False, **kwargs) 我避免了所有文件写出。调试器会将.out文件写入目录中,Python文件是optimize标志的结果。
虽然这暂时可行,但我并不完全满意这种方法。假定有一种方法能够保持优化同时保持工作目录干净,那将导致更好的性能,因此这样做将是更好的解决方案。如果有人有更好的方法,我非常乐意尝试。

-1

显然,在ply.yacc中有关于这个的参数:

def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, 
     check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='',
     debuglog=None, errorlog = None, picklefile=None):

所以,您只需要传递一个不同的错误日志和调试日志(具有不会打印到stdout/stderr的debug()等方法)。并且您需要指定一个固定的输出目录。那就是您需要做的全部。

更新:我刚刚检查了一下,这是正确的设置:

yacc.yacc(
    debug=False,                         # do not create parser.out
    outputdir=r"c:\temp\aaa" # instruct to place parsetab here
)

实际上,你需要使用一个已经包含parsetab.py的outputdir。这样不仅可以消除这个消息,而且你的程序也不会写出parsetab.py文件,它只会使用它。

1
OP并不是要求简单地不显示消息,而是不要一直重新生成临时解析器表文件。 - Elias Dorneles
我猜当你使用一个固定的目录并且已经把文件放在那里时,它们就不会被重新生成。但我会尝试并让你知道结果。 - nagylzs
debug=False 确实可以抑制错误消息,但无法抑制文件生成。当我将其与部署位置一起使用(文件已经存在的地方),它仍然会尝试覆盖它们。 - Michael
如果您想完全禁用文件生成,则可以覆盖 LRGeneratedTable.write_table 方法(例如,将其变成一个什么也不做的方法)。或者,您可以使用 picklefile 参数(传递类似文件的对象),并覆盖 LRGeneratedTable.pickle_table 以什么也不做。不幸的是,LRGeneratedTable 类的使用被硬编码到 yacc.yacc 中,因此您无法对其进行子类化。 :-( - nagylzs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接