如何指定一个包含两部分的 pyparsing 表达式,每个部分的长度可能不同,但它们的总和是固定的?

问题描述 投票:0回答:1

我需要指定一个定义为

ambn
的表达式,这样:

  • m
    +
    n
    =
    t
    t
    已修复
  • 0 <=
    m
    <=
    t
    - 1
  • 1 <=
    n
    <=
    t

这是当前代码的样子,经过简化:

from pyparsing import Char, Combine

def ab(t):
  first = Char('a')[0, t - 1]
  second = Char('b')[1, t]
  
  expression = first + second
  expression.add_condition(
    lambda result: len(result) == t,
    message = f'Total length must be {t}'
  )
    
  return Combine(expression)

然而,表达式消耗了它能找到的所有内容,并对该结果调用条件函数而不回溯。例如:

grammar = (ab(4) | Char('b'))[1, ...].set_name('grammar')
grammar.parse_string('abbbb', parse_all = True)
ParseException: Expected grammar, found 'abbbb'  (at char 0), (line:1, col:1)

我想要的结果是

['abbb', 'b']
,如果将有问题的表达式指定为:

expression = Or([
  Char('a')[m] + Char('b')[t - m]
  for m in range(0, t)
])

...但这看起来不必要的冗长。

有更好的方法吗?

python pyparsing
1个回答
0
投票

我使用“|”重写了你的第二种方法运算符,创建 MatchFirst 表达式而不是 Or:

a = pp.Char('a')
b = pp.Char('b')

def ab(t):
    expr = b[t]
    for i in range(1, t):
        expr |= a[i] + b[t-i]
    return pp.Combine(expr)

它还使用

b[t]
定义第一项,并使用从 1 开始的范围,而不是默认的 0。

然后我使用 run_tests 进行测试:

ab5 = ab(5)
ab5.run_tests("""\
    bbbbb
    abbbb
    aabbb
    aaabb
    aaaab
    """)

ab4b = ab(4) + b
ab4b.run_tests("""\
    bbbbb
    abbbb
    aabbb
    aaabb
    """)

两种表达方式都给出了预期的结果。

您使用条件的第一种方法因您所说的确切原因而失败 - 'b' 的重复不知道何时停止,因此它会读取超出并进入 'b' ,而 'b' 旨在被解析为第二项。 (与正则表达式不同,pyparsing 不执行任何回溯)。

因此,我没有使用条件,而是将 ab() 的 b 项更改为 Forward,并向 a 项添加了解析操作,以将表达式插入到包含正确数量的 b 的 b 项中。

def ab(t):
    a_s = a[0, t-1]
    b_s = pp.Forward()
    expr = a_s + b_s

    def dynamic_b_term(a_chars):
        b_s << b[t - len(a_chars)]

    a_s.add_parse_action(dynamic_b_term)

    return pp.Combine(expr)

这也通过了

ab(5)
ab(4) + b
的 run_tests。

© www.soinside.com 2019 - 2024. All rights reserved.