После публикации моего комментария, ссылающегося на пример invRegex в pyparsing, я посмотрел немного ближе к вашему вводу, и выглядело так, как будто вы можете интерпретировать это как инфиксную запись, с ',' и '-' как бинарные операторы.В 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, которая поддерживает доступ к анализируемым токенам в виде элементов в списке, элементов в dict или атрибутов в объекте.Вызывая 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 Regex делает это с помощью путаницы генераторов - надеюсь, будет достаточно некоторой простой рекурсии.)