Ray*_*szt 9 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
Run Code Online (Sandbox Code Playgroud)
所以'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)
Run Code Online (Sandbox Code Playgroud)
现在,我想到的是,当它击中每个范围时,它会跟踪每个"级别",最终我会得到一个包含嵌套范围的结构.但是,它出错了
$scope module extralevel $end
Run Code Online (Sandbox Code Playgroud)
说它预计'$ upscope'.
所以我知道我没有正确使用递归.有人可以帮我吗?如果我需要提供更多信息,请告诉我.
谢谢!!!!
根据您的定义,范围不能包含另一个范围,后跟一些映射,后跟另一个范围.
如果解析器具有调试模式,它将打印其解析树,您将能够立即看到它.但简而言之,你说的是有零个或多个映射,后跟零个或多个范围,后跟零个或多个映射,所以如果有一个范围,后跟一个映射,你已经通过了范围字段,所以任何更多的范围都是无效的.如果pyparsing使用的语言支持"或",您可以使用:
scope << (scope_header + ZeroOrMore((wire_map | scope)) + scope_footer)
Run Code Online (Sandbox Code Playgroud)
请选择@ ZackBloom的答案作为正确的答案,他直接将其直接理解,甚至不知道pyparsing的语法.
关于你的语法的一些评论/建议:
通过上面的答案,您可以在ParseResults上使用pprint和pyparsing的asList()方法可视化嵌套:
res = scope.parseString(vcd)
from pprint import pprint
pprint(res.asList())
Run Code Online (Sandbox Code Playgroud)
赠送:
[[['$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']]]
Run Code Online (Sandbox Code Playgroud)
所以现在你有了很好的结构.但是你可以稍微清理一下.一方面,现在你有结构的,你并不真的需要所有这些$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)
Run Code Online (Sandbox Code Playgroud)
(不需要分组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']]]
Run Code Online (Sandbox Code Playgroud)
冒着分组太多的风险,我会建议Group你的scope表达内容,如下所示:
scope << Group(scope_header +
Group(ZeroOrMore((wire_map | scope))) +
scope_footer)
Run Code Online (Sandbox Code Playgroud)
这给出了以下结果:
[[['module', 'toplevel'],
[[['module', 'midlevel'], [['wire', 'a'], ['wire', 'b']]],
['wire', 'c'],
['wire', 'd'],
['wire', 'e'],
[['module', 'extralevel'], [['wire', 'f'], ['wire', 'g']]],
['wire', 'h'],
['wire', 'i']]]]
Run Code Online (Sandbox Code Playgroud)
现在每个范围结果都有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])
Run Code Online (Sandbox Code Playgroud)
它看起来像:
- toplevel
- midlevel
wire: a
wire: b
wire: c
wire: d
wire: e
- extralevel
wire: f
wire: g
wire: h
wire: i
Run Code Online (Sandbox Code Playgroud)
好的第一个问题,欢迎来到SO和pyparsing!