python - pyparsing 如何跳过缩进 block 的结尾?

标签 python pyparsing

我正在尝试使用 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)

但我很难定义 SkipTo 子句的 next_identifier,因为标识符可能会自由出现在描述文本中。

看来我需要在语法中包含缩进,以便我可以跳到下一个非缩进行。

我试过:

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)

第 55 个字符在连续行的最开始:

...will wrap\n    on to the next line...
              ^

这看起来有点奇怪,因为该字符位置后面显然跟着空格,这使它成为一个缩进的子条目。

我在 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()
    )
)

...适用于所提供示例的方法不适用于我的情况,因为它将使用后续定义。

最佳答案

当您使用 indentedBlock 时,您传入的参数是 block 中每一行的表达式,因此它不应该是 indentedBlock(ZeroOrMore(line_expression), stack),只是 indentedBlock(line_expression, stack)。 Pyparsing 包含一个用于“从这里到行尾的所有内容”的内置表达式,标题为 restOfLine,因此我们仅将其用于缩进 block 中每一行的表达式:

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

关于python - pyparsing 如何跳过缩进 block 的结尾?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47484639/

相关文章:

python - 从 __init__.py 中导入类以生成更短的导入语句是否存在问题?

python - 如何在 Python 中获取目录中的文件名列表

python - 对点分布进行分类以进行对象识别

python - open() 如何在有和没有 `with` 的情况下工作?

unit-testing - 如何断言 pyparsing.ParseResults 内容?

parsing - 重写多用途日志文件解析器以使用形式语法会提高可维护性吗?

python - PyParsing 和多行系统日志消息

python - python 中的曲线拟合

python - "A or B or both"的表达式组合

python - 是否可以使用 pyparsing 解析非平凡的 C 枚举?