2011-01-26 20 views
14

Chciałbym użyć pyparsing do parsowania wyrażenia formularza: expr = '(gimme [some {nested [lists]}])', i wracam do listy Pythona formularza: [[['gimme', ['some', ['nested', ['lists']]]]]]. Teraz moja gramatyka wygląda następująco:Jak używać pyparsing do parsowania wyrażeń zagnieżdżonych, które mają wiele typów otwieracza/zamykania?

nestedParens = nestedExpr ('(', ')')
nestedBrackets = nestedExpr ('[', ']')
nestedCurlies = nestedExpr ('{', „} ')
w załączeniu = zagnieżdżonyParens | nestedBrackets | nestedCurlies

Obecnie enclosed.searchString(expr) zwraca listę w formularzu: [[['gimme', ['some', '{nested', '[lists]}']]]]. Nie tego chcę, ponieważ nie rozpoznaję kwadratowych lub nawiasów klamrowych, ale nie wiem dlaczego.

Odpowiedz

26

Oto rozwiązanie pyparsing który wykorzystuje samodzielne modyfikowanie gramatyki dynamicznie dopasować właściwy znak zamykający nawias.

from pyparsing import * 

data = '(gimme [some {nested, nested [lists]}])' 

opening = oneOf("({ [") 
nonBracePrintables = ''.join(c for c in printables if c not in '(){}[]') 
closingFor = dict(zip("({[",")}]")) 
closing = Forward() 
# initialize closing with an expression 
closing << NoMatch() 
closingStack = [] 
def pushClosing(t): 
    closingStack.append(closing.expr) 
    closing << Literal(closingFor[t[0]]) 
def popClosing(): 
    closing << closingStack.pop() 
opening.setParseAction(pushClosing) 
closing.setParseAction(popClosing) 

matchedNesting = nestedExpr(opening, closing, Word(alphas) | Word(nonBracePrintables)) 

print matchedNesting.parseString(data).asList() 

drukuje:

[['gimme', ['some', ['nested', ',', 'nested', ['lists']]]]] 

aktualizacja: napisałem wyżej rozwiązanie, bo rzeczywiście napisany go ponad rok temu jako eksperyment. Po prostu przyjrzałem się bliżej oryginalnemu wpisowi i przypomniałem sobie definicję typu rekursywnego utworzoną za pomocą metody operatorPrecedence, a więc zmieniłem to rozwiązanie, używając oryginalnego podejścia - o wiele prostszego do naśladowania! (Może mieć problem od lewej rekursji z prawej danych wejściowych choć nie dokładnie testowane):

from pyparsing import * 

enclosed = Forward() 
nestedParens = nestedExpr('(', ')', content=enclosed) 
nestedBrackets = nestedExpr('[', ']', content=enclosed) 
nestedCurlies = nestedExpr('{', '}', content=enclosed) 
enclosed << (Word(alphas) | ',' | nestedParens | nestedBrackets | nestedCurlies) 


data = '(gimme [some {nested, nested [lists]}])' 

print enclosed.parseString(data).asList() 

Daje:

[['gimme', ['some', ['nested', ',', 'nested', ['lists']]]]] 
+0

Paul, dziękuję bardzo za odpowiedź informacyjną. I jeszcze więcej dziękuję za tworzenie i otwieranie mojej nowej ulubionej biblioteki Pythona! pyparsing pomaga mi radykalnie zmniejszyć rozmiar, złożoność i łatwość obsługi projektu, nad którym pracuję. – Derek

+0

Dzięki za komplementy, witamy w pyparstwie! – PaulMcG

-3

To powinno załatwić sprawę. Testowałem go na przykład:

import re 
import ast 

def parse(s): 
    s = re.sub("[\{\(\[]", '[', s) 
    s = re.sub("[\}\)\]]", ']', s) 
    answer = '' 
    for i,char in enumerate(s): 
     if char == '[': 
      answer += char + "'" 
     elif char == '[': 
      answer += "'" + char + "'" 
     elif char == ']': 
      answer += char 
     else: 
      answer += char 
      if s[i+1] in '[]': 
       answer += "', " 
    ast.literal_eval("s=%s" %answer) 
    return s 

komentarz, jeśli potrzebujesz więcej

+1

przeprosiny za nie jest wystarczająco jasne, ale wyjście miałem na myśli to zagnieżdżona lista pythonów, która jest częstym wynikiem parsowania wyrażeń zagnieżdżonych z pyparsingiem. Twoje rozwiązanie właśnie zwraca ciąg, który wygląda jak drukowana lista pytonów. Dziękujemy za pomoc! – Derek

+0

@Derek: Nie zwracam ciągu. Zwracam listę. Zmienna o nazwie answer jest łańcuchem, tak; ale właśnie dlatego istnieje ta linia, która mówi exec "s =% s"% odpowiedzi. Spowoduje to utworzenie nowej zmiennej o nazwie s, która jest listą. Właśnie dlatego mój kod zwraca s, a nie odpowiedź. Powinieneś sprawdzić typ zwróconej wartości, a zobaczysz, że jest to lista, a nie ciąg znaków. – inspectorG4dget

+3

. Zwracasz listę, ale myślę, że źle zrozumiałeś, co parsowanie jest w tym kontekście. Podczas analizowania łańcucha zazwyczaj masz dostęp do dopasowanych tokenów/grup w czasie analizy, co pozwala wykonać na nich pewne działanie. Twój program dynamicznie generuje kod Pythona i uruchamia go, aby przekształcić łańcuch znaków na listę zagnieżdżoną. Nie parsuje niczego, ani nie używa pyparsingu, jak wspomniano w pierwotnym pytaniu. Nie wspominając już o tym, wykona arbitralny kod Pythona, więc na przykład nie uda mu się wejść z cudzysłowami. – Derek

Powiązane problemy