我试图用pyparsing
解析这样的结构:
identifier: some description text here which will wrap
on to the next line. the follow-on text should be
indented. it may contain identifier: and any text
at all is allowed
next_identifier: more description, short this time
last_identifier: blah blah
我需要类似的东西:
import pyparsing as pp
colon = pp.Suppress(':')
term = pp.Word(pp.alphanums + "_")
description = pp.SkipTo(next_identifier)
definition = term + colon + description
grammar = pp.OneOrMore(definition)
但我很难定义next_identifier
子句的SkipTo
,因为标识符可能会在描述文本中自由出现。
似乎我需要在语法中包含缩进,以便我可以跳过下一个非缩进行。
我试过了:
description = pp.Combine(
pp.SkipTo(pp.LineEnd()) +
pp.indentedBlock(
pp.ZeroOrMore(
pp.SkipTo(pp.LineEnd())
),
indent_stack
)
)
但我收到错误:
ParseException: not a subentry (at char 55), (line:2, col:1)
Char 55正处于运行线的最开始处:
...will wrap\n on to the next line...
^
这看起来有点奇怪,因为这个char位置后面跟着明显的空格,这使得它成为一个缩进的子条目。
我在ipdb中的回溯看起来像:
5311 def checkSubIndent(s,l,t):
5312 curCol = col(l,s)
5313 if curCol > indentStack[-1]:
5314 indentStack.append( curCol )
5315 else:
-> 5316 raise ParseException(s,l,"not a subentry")
5317
ipdb> indentStack
[1]
ipdb> curCol
1
我应该补充说,我匹配的整个结构也可以缩进(未知数量),所以解决方案如下:
description = pp.Combine(
pp.SkipTo(pp.LineEnd()) + pp.LineEnd() +
pp.ZeroOrMore(
pp.White(' ') + pp.SkipTo(pp.LineEnd()) + pp.LineEnd()
)
)
...适用于所提供的示例在我的情况下不起作用,因为它将使用后续定义。
答案 0 :(得分:1)
当您使用indentedBlock
时,您传入的参数是块中每一行的表达式,因此它不应该是indentedBlock(ZeroOrMore(line_expression), stack)
,而只是indentedBlock(line_expression, stack)
。 Pyparsing包含一个内置表达式,用于“从这里到行尾的所有内容”,标题为restOfLine
,因此我们只将其用于缩进块中每行的表达式:
import pyparsing as pp
NL = pp.LineEnd().suppress()
label = pp.ungroup(pp.Word(pp.alphas, pp.alphanums+'_') + pp.Suppress(":"))
indent_stack = [1]
# see corrected version below
#description = pp.Group((pp.Empty()
# + pp.restOfLine + NL
# + pp.ungroup(pp.indentedBlock(pp.restOfLine, indent_stack))))
description = pp.Group(pp.restOfLine + NL
+ pp.Optional(pp.ungroup(~pp.StringEnd()
+ pp.indentedBlock(pp.restOfLine,
indent_stack))))
labeled_text = pp.Group(label("label") + pp.Empty() + description("description"))
我们使用ungroup删除indentedBlock
创建的额外嵌套级别,但我们还需要删除indentedBlock
内部创建的每行嵌套。我们通过解析操作执行此操作:
def combine_parts(tokens):
# recombine description parts into a single list
tt = tokens[0]
new_desc = [tt.description[0]]
new_desc.extend(t[0] for t in tt.description[1:])
# reassign rebuild description into the parsed token structure
tt['description'] = new_desc
tt[1][:] = new_desc
labeled_text.addParseAction(combine_parts)
此时,我们已经完成了很多工作。以下是您解析和转储的示例文本:
parsed_data = (pp.OneOrMore(labeled_text)).parseString(sample)
print(parsed_data[0].dump())
['identifier', ['some description text here which will wrap', 'on to the next line. the follow-on text should be', 'indented. it may contain identifier: and any text', 'at all is allowed']]
- description: ['some description text here which will wrap', 'on to the next line. the follow-on text should be', 'indented. it may contain identifier: and any text', 'at all is allowed']
- label: 'identifier'
或者这个代码来提取标签和描述字段:
for item in parsed_data:
print(item.label)
print('..' + '\n..'.join(item.description))
print()
identifier
..some description text here which will wrap
..on to the next line. the follow-on text should be
..indented. it may contain identifier: and any text
..at all is allowed
next_identifier
..more description, short this time
last_identifier
..blah blah