PLY:快速解析长项列表?

时间:2011-06-20 20:01:38

标签: python parsing optimization ply

我正在PLY中使用一个相当简单的解析器,我的一个规则采用以下形式:

def p_things(p):
    '''
    things : thing things
    things : thing
    '''
    p[0] = [p[1]]
    if len(p) == 3:
        p[0] += p[2]

输入文件通常是thing s的简单列表,因此解析本身并不复杂。但是我的一些输入文件非常大(相当规律地超过100,000行,极端情况下超过1,000,000行)。在分析中(通过cProfile and pstats),通过重复调用p_things来占用大部分运行时 - 可能是对things列表中的每个项目进行一次调用。

有没有办法减少这个时间,或者更有效的方法来构建这个规则?到目前为止我看到的大多数答案(以及我发现的规范编译器信息)都将此方法列为构建可解析项列表的普遍接受的方法,无论长度如何。

3 个答案:

答案 0 :(得分:8)

原来我忘记了一些基本的编译器理论。 PLY是一个LALR(1)解析器,因此最好将规则写为:

def p_things(p):
    '''
    things : things thing
    things : thing
    '''
    if len(p) == 2:
        p[0] = [p[1]]
    else:
        p[0] = p[1]
        p[0].append(p[2])

虽然它可能看起来更冗长,但实际上有一个显着的改进 - 在PLY或Python的某个地方,解析器能够在左递归形式上应用一些优化。我看到我的较大输入文件的性能从指数下降到线性;一个样本,things列表中有超过一百万个项目,在不到20%的时间内运行。

答案 1 :(得分:0)

在不更改代码的情况下,您可以尝试使用python的“PyPy”版本进行即时编译 - 它可能比普通的CPython更快地运行代码。

答案 2 :(得分:0)

所以总结一下:

  • 性能问题与解析器本身无关(但与+=的使用有关)
  • 通过使RHS明确无误,使LALR(1)解析器更轻松。
  • 最好在规则中尽量避免不必要的选择语句(ifs)。

为了更好地了解Ioannis Filippidis' comment,更容易对其进行可视化显示。这就是我认为的意思,也差不多是我最终得到的结果。

def p_things_iter(p):
    '''
    things_iter : things thing
    '''
        p[0] = p[1]
        p[0].append(p[2])

def p_things_end(p):
    '''
    things_iter : thing
    '''
    p[0] = [p[1]]

def p_things(p):
    '''
    things : things_iter
    things : things_end
    '''
    p[0] = p[1]