pyparsing,forward和recursion

时间:2010-11-10 03:07:55

标签: python recursion forward pyparsing

我正在使用pyparsing来解析vcd(值更改转储)文件。基本上,我想读取文件,将其解析为内部字典,并操纵值。

没有详细介绍结构,我的问题就是识别嵌套类别。

在vcd文件中,您有“范围”,其中包括连线和可能更深的(嵌套)范围。把它们想象成水平。

所以在我的文件中,我有:

$scope module toplevel $end
$scope module midlevel $end
$var wire a $end
$var wire b $end
$upscope $end
$var wire c $end
$var wire d $end
$var wire e $end
$scope module extralevel $end
$var wire f $end
$var wire g $end
$upscope $end
$var wire h $end
$var wire i $end
$upscope $end

所以'toplevel'包含所有东西(a - i),'midlevel'包含(a - b),'extralevel'包含(f - g)等等。

以下是解析此部分的代码(代码段):

scope_header = Group(Literal('$scope') + Word(alphas) + Word(alphas) + \
                     Literal('$end'))

wire_map = Group(Literal('$var') + Literal('wire') + Word(alphas) + \
                 Literal('$end'))

scope_footer = Group(Literal('$upscope') + Literal('$end'))

scope = Forward()
scope << (scope_header + ZeroOrMore(wire_map) + ZeroOrMore(scope) + \
          ZeroOrMore(wire_map) + scope_footer)

现在,我想到发生的事情是,当它击中每个范围时,它会跟踪每个“级别”,最终会得到一个包含嵌套范围的结构。但是,它出错了

$scope module extralevel $end

说它期待'$ upscope'。

所以我知道我没有正确使用递归。有人可以帮我吗?如果我需要提供更多信息,请告诉我。

感谢!!!!

3 个答案:

答案 0 :(得分:9)

根据您的定义,范围不能包含另一个范围,后面跟一些地图,后跟另一个范围。

如果解析器具有打印其解析树的调试模式,您将能够立即看到它。但简而言之,你说的是有零个或多个映射,后跟零个或多个范围,后跟零个或多个映射,所以如果有一个范围,后跟一个映射,你已经通过了范围字段,所以任何更多的范围都是无效的。如果pyparsing使用的语言支持“或”,则可以使用:

scope << (scope_header + ZeroOrMore((wire_map | scope)) + scope_footer)

答案 1 :(得分:6)

请选择@ ZackBloom的答案作为正确的答案,他直接理解它,甚至不知道pyparsing的语法。

关于你的语法的一些意见/建议:

通过上面发布的答案,您可以在ParseResults上使用pprint和pyparsing的asList()方法可视化嵌套:

res = scope.parseString(vcd)

from pprint import pprint
pprint(res.asList())

,并提供:

[[['$scope', 'module', 'toplevel', '$end'],
  [['$scope', 'module', 'midlevel', '$end'],
   ['$var', 'wire', 'a', '$end'],
   ['$var', 'wire', 'b', '$end'],
   ['$upscope', '$end']],
  ['$var', 'wire', 'c', '$end'],
  ['$var', 'wire', 'd', '$end'],
  ['$var', 'wire', 'e', '$end'],
  [['$scope', 'module', 'extralevel', '$end'],
   ['$var', 'wire', 'f', '$end'],
   ['$var', 'wire', 'g', '$end'],
   ['$upscope', '$end']],
  ['$var', 'wire', 'h', '$end'],
  ['$var', 'wire', 'i', '$end'],
  ['$upscope', '$end']]]

所以现在你的结果很好。但是你可以稍微清理一下。首先,既然你有结构,那么你真的不需要所有那些$scope$end等等。当你浏览解析后的结果时,你当然可以跳过它们,但你也可以让pyparsing从解析的输出中删除它们(因为结果现在是结构化的,你并没有真正丢失任何东西)。将解析器定义更改为:

SCOPE, VAR, UPSCOPE, END = map(Suppress, 
                                 "$scope $var $upscope $end".split())
MODULE, WIRE = map(Literal, "module wire".split())

scope_header = Group(SCOPE + MODULE + Word(alphas) + END)
wire_map = Group(VAR + WIRE + Word(alphas) + END) 
scope_footer = (UPSCOPE + END) 

(无需分组scope_footer - 该表达式中的所有内容都被禁止,因此Group只会为您提供一个空列表。)

现在你可以更清楚地看到真正重要的部分:

[[['module', 'toplevel'],
  [['module', 'midlevel'], ['wire', 'a'], ['wire', 'b']],
  ['wire', 'c'],
  ['wire', 'd'],
  ['wire', 'e'],
  [['module', 'extralevel'], ['wire', 'f'], ['wire', 'g']],
  ['wire', 'h'],
  ['wire', 'i']]]

冒着分组过多的风险,我建议Group scope表达式的内容,如下所示:

scope << Group(scope_header + 
               Group(ZeroOrMore((wire_map | scope))) + 
               scope_footer) 

给出了这些结果:

[[['module', 'toplevel'],
  [[['module', 'midlevel'], [['wire', 'a'], ['wire', 'b']]],
   ['wire', 'c'],
   ['wire', 'd'],
   ['wire', 'e'],
   [['module', 'extralevel'], [['wire', 'f'], ['wire', 'g']]],
   ['wire', 'h'],
   ['wire', 'i']]]]

现在每个范围结果都有2个可预测的元素:模块标题和电线或子范围列表。这种可预测性将使编写将导航结果的递归代码变得更加容易:

res = scope.parseString(vcd)
def dumpScope(parsedTokens, indent=''):
    module,contents = parsedTokens
    print indent + '- ' + module[1]
    for item in contents:
        if item[0]=='wire':
            print indent + '  wire: ' + item[1]
        else:
            dumpScope(item, indent+'  ')
dumpScope(res[0])

看起来像:

- toplevel
  - midlevel
    wire: a
    wire: b
  wire: c
  wire: d
  wire: e
  - extralevel
    wire: f
    wire: g
  wire: h
  wire: i

好的第一个问题,欢迎来到SO和pyparsing!

答案 2 :(得分:1)

我知道这是一个已经回答的旧问题,但我想我会添加一个有用的链接到Python VCD解析包,它可以将你的VCD放到基本的Python数据结构中。

Verilog_VCD 1.0