在发表我对 pyparsing 的 invRegex 示例的评论后,我仔细查看了您的输入,看起来您可以将其解释为中缀表示法,使用 ',' 和 '-' 作为二进制运算符。 Pyparsing 有一个笨拙地命名为 operatorPrecedence 的辅助方法,它根据运算符的优先级解析表达式,并在括号中分组。 (这比仅使用 nestedExpr 帮助器方法更聪明,它匹配嵌套在分组符号中的表达式。)所以这是一个使用 operatorPrecedence 的解析器的入门版本:
data = """\
(A,B)-C-D
A-B-(C,D)
A-(B,C,D-(E,F,G,H,I))""".splitlines()
from pyparsing import alphas, oneOf, operatorPrecedence, opAssoc
node = oneOf(list(alphas))
graphExpr = operatorPrecedence(node,
[
('-', 2, opAssoc.LEFT),
(',', 2, opAssoc.LEFT),
])
for d in data:
print graphExpr.parseString(d).asList()
Pyparsing 实际上返回一个 ParseResults 类型的复杂结构,它支持将解析的标记作为列表中的元素、字典中的项目或对象中的属性来访问。通过调用asList,我们只是获取了简单列表形式的元素。
上面的输出表明我们看起来是在正确的轨道上:
[[['A', ',', 'B'], '-', 'C', '-', 'D']]
[['A', '-', 'B', '-', ['C', ',', 'D']]]
[['A', '-', ['B', ',', 'C', ',', ['D', '-', ['E', ',', 'F', ',', 'G', ',', 'H', ',', 'I']]]]]
Pyparsing 还允许您将回调或parse actions 附加到单个表达式,以便在解析时调用。例如,这个解析动作将解析时转换为整数:
def toInt(tokens):
return int(tokens[0])
integer = Word(nums).setParseAction(toInt)
在 ParseResults 中返回值时,它已经被转换为整数。
类也可以被指定为解析动作,ParseResults 对象被传递给类的__init__ 方法并返回结果对象。我们可以在 operatorPrecedence 中指定解析操作,方法是在每个操作符的描述符元组中添加解析操作作为第 4 个元素。
这是二元运算符的基类:
class BinOp(object):
def __init__(self, tokens):
self.tokens = tokens
def __str__(self):
return self.__class__.__name__ + str(self.tokens[0][::2])
__repr__ = __str__
从这个基类中,我们可以派生出 2 个子类,- 和 , 的每个运算符都有一个:
class Path(BinOp):
pass
class Branch(BinOp):
pass
并将它们添加到 operatorPrecedence 中的运算符定义元组中:
node = oneOf(list(alphas))
graphExpr = operatorPrecedence(node,
[
('-', 2, opAssoc.LEFT, Path),
(',', 2, opAssoc.LEFT, Branch),
])
for d in data:
print graphExpr.parseString(d).asList()
这为我们提供了每个输入字符串的嵌套对象结构:
[Path[Branch['A', 'B'], 'C', 'D']]
[Path['A', 'B', Branch['C', 'D']]]
[Path['A', Branch['B', 'C', Path['D', Branch['E', 'F', 'G', 'H', 'I']]]]]
从此结构生成路径留作 OP 练习。 (pyparsing 正则表达式逆变器使用一堆生成器来执行此操作 - 希望一些简单的递归就足够了。)