Acceda a los elementos analizados utilizando Pyparsing

Tengo un montón de frases que necesito analizar y convertir al código de búsqueda de expresiones regulares correspondiente. Ejemplos de mis oraciones –

LINE_CONTAINS phrase one BEFORE {phrase2 AND phrase3} AND LINE_STARTSWITH Therefore we

-Esto significa en la línea, phrase one viene en algún lugar antes de phrase2 y phrase3 . Además, la línea debe comenzar con Therefore we

LINE_CONTAINS abc {upto 4 words} xyz {upto 3 words} pqr

-Esto significa que debo permitir hasta 4 palabras entre las 2 primeras frases y hasta 3 palabras entre las 2 últimas frases

Usando la ayuda de Paul Mcguire ( aquí ), se escribió la siguiente gramática:

 from pyparsing import (CaselessKeyword, Word, alphanums, nums, MatchFirst, quotedString, infixNotation, Combine, opAssoc, Suppress, pyparsing_common, Group, OneOrMore, ZeroOrMore) LINE_CONTAINS, LINE_STARTSWITH = map(CaselessKeyword, """LINE_CONTAINS LINE_STARTSWITH """.split()) NOT, AND, OR = map(CaselessKeyword, "NOT AND OR".split()) BEFORE, AFTER, JOIN = map(CaselessKeyword, "BEFORE AFTER JOIN".split()) lpar=Suppress('{') rpar=Suppress('}') keyword = MatchFirst([LINE_CONTAINS, LINE_STARTSWITH, LINE_ENDSWITH, NOT, AND, OR, BEFORE, AFTER, JOIN]) # declaring all keywords and assigning order for all further use phrase_word = ~keyword + (Word(alphanums + '_')) upto_N_words = Group(lpar + 'upto' + pyparsing_common.integer('numberofwords') + 'words' + rpar) phrase_term = Group(OneOrMore(phrase_word) + ZeroOrMore((upto_N_words) + OneOrMore(phrase_word)) phrase_expr = infixNotation(phrase_term, [ ((BEFORE | AFTER | JOIN), 2, opAssoc.LEFT,), # (opExpr, numTerms, rightLeftAssoc, parseAction) (NOT, 1, opAssoc.RIGHT,), (AND, 2, opAssoc.LEFT,), (OR, 2, opAssoc.LEFT), ], lpar=Suppress('{'), rpar=Suppress('}') ) # structure of a single phrase with its operators line_term = Group((LINE_CONTAINS | LINE_STARTSWITH | LINE_ENDSWITH)("line_directive") + Group(phrase_expr)("phrase")) # basically giving structure to a single sub-rule having line-term and phrase line_contents_expr = infixNotation(line_term, [(NOT, 1, opAssoc.RIGHT,), (AND, 2, opAssoc.LEFT,), (OR, 2, opAssoc.LEFT), ] ) # grammar for the entire rule/sentence sample1 = """ LINE_CONTAINS phrase one BEFORE {phrase2 AND phrase3} AND LINE_STARTSWITH Therefore we """ sample2 = """ LINE_CONTAINS abcd {upto 4 words} xyzw {upto 3 words} pqrs BEFORE something else """ 

Mi pregunta ahora es: ¿Cómo accedo a los elementos analizados para convertir las oraciones a mi código de expresiones regulares? Para esto, he intentado lo siguiente:

 parsed = line_contents_expr.parseString(sample1)/(sample2) print (parsed[0].asDict()) print (parsed) pprint.pprint(parsed) 

El resultado del código anterior para sample1 fue:

{}

[[[‘LINE_CONTAINS’, [[[‘frase’, ‘one’], ‘ANTES’, [[‘frase2’], ‘AND’, [‘frase3’]]]]], ‘AND’, [‘ LINE_STARTSWITH ‘, [[‘ Por lo tanto, ‘nosotros’]]]]

([[[[[[[[[Oración], [‘]], {}),’ ANTES ‘, ([[[[frase]]], {}),’ AND ‘, ([‘frase3’], {})], {})], {})], {})], {‘ frase ‘: [(([([[[oración], ‘uno’], { }), ‘ANTES’, ([([‘frase2’], {}), ‘AND’, ([‘frase3’], {})], {})], {})], {}), 1)], ‘ line_directive ‘: [(‘LINE_CONTAINS’, 0)]}), ‘AND’, ([‘LINE_STARTSWITH’, ([([”, ‘we’], {})], {} )], {‘ frase ‘: [(([[[[Por lo tanto ‘,’ nosotros ‘], {})], {}), 1)],’ line_directive ‘: [(‘ LINE_STARTSWITH ‘, 0)]} )], {})], {})

El resultado del código anterior para sample2 fue:

{‘ frase ‘: [[[‘abcd’, {‘ numberofwords ‘: 4}, ‘xyzw’, {‘ numberofwords ‘: 3}, ‘pqrs’], ‘BEFORE’, [‘algo’, ‘else’] ]], ‘ line_directive ‘: ‘LINE_CONTAINS’}

[[‘LINE_CONTAINS’, [[[‘abcd’, [‘upto’, 4, ‘words’], ‘xyzw’, [‘upto’, 3, ‘words’], ‘pqrs’], ‘BEFORE’, [‘algo más’]]]]]

([([‘LINE_CONTAINS’, ([[[[[abcd ‘, ([‘ upto ‘, 4,’ words ‘], {‘ numberofwords ‘: [(4, 1)]}),’ xyzw ‘, ([‘hasta’, 3, ‘palabras’], {‘ numberofwords ‘: [(3, 1)]}), ‘pqrs’], {}), ‘BEFORE’, ([‘something’, ‘else’ ], {})], {})], {})], {‘frase’: [(([[[[[[[abcd ‘, ([‘ upto ‘, 4,’ words ‘], {‘ numberofwords ‘: [(4, 1)]}),’ xyzw ‘, ([‘ upto ‘, 3,’ words ‘], {‘ numberofwords ‘: [(3, 1)]}),’ pqrs ‘], { }), ‘ANTES’, ([‘algo’, ‘else’], {})], {})], {}), 1)], ‘ line_directive ‘: [(‘LINE_CONTAINS’, 0)]} )], {})

Mis preguntas basadas en la salida anterior son:

  1. ¿Por qué la impresión (impresión bonita) tiene un análisis más detallado que la impresión normal?
  2. ¿Por qué el método asDict() no da salida para sample1 pero sí para sample2 ?
  3. Cuando trato de acceder a los elementos analizados utilizando print (parsed.numberofwords) o parsed.line_directive o parsed.line_term , no me da nada. ¿Cómo puedo acceder a estos elementos para usarlos para construir mis códigos de expresiones regulares?

Para responder a sus preguntas de impresión. 1) pprint está ahí para imprimir bastante una lista anidada de tokens, sin mostrar ningún nombre de resultados; es esencialmente un envolvente para llamar a pprint.pprint(results.asList()) . 2) asDict() está allí para realizar la conversión de los resultados analizados a un dictado de Python real, por lo que solo muestra los nombres de los resultados (con anidación si tiene nombres dentro de los nombres).

Para ver el contenido de su salida analizada, es mejor que use print(result.dump()) . dump() muestra el anidamiento de los resultados y los resultados con nombre en el camino.

 result = line_contents_expr.parseString(sample2) print(result.dump()) 

También recomiendo usar expr.runTests para darle salida dump() , así como cualquier excepción y localizadores de excepciones. Con su código, puede hacerlo más fácilmente usando:

 line_contents_expr.runTests([sample1, sample2]) 

Pero también sugiero que retroceda un segundo y piense de qué se trata este negocio de {upto n words} . Mire sus muestras y dibuje rectangularjs alrededor de los términos de línea, y luego dentro de los términos de línea dibuje círculos alrededor de los términos de la frase. (Este sería un buen ejercicio antes de escribir una descripción BNF de esta gramática, que siempre recomiendo como un paso para solucionar el problema). ¿Qué pasaría si tratara las expresiones upto como otro? ¿operador? Para ver esto, cambia phrase_term nuevo a la forma en que lo tenías:

 phrase_term = Group(OneOrMore(phrase_word)) 

Y luego cambie su primera entrada de precedencia al definir una expresión de frase a:

  ((BEFORE | AFTER | JOIN | upto_N_words), 2, opAssoc.LEFT,), 

O piense que tal vez tenga upto operador en una prioridad más alta o más baja que ANTES, DESPUÉS, Y ÚNETE, y ajuste la lista de prioridades en consecuencia.

Con este cambio, obtengo esta salida de llamar a RunTests en sus muestras:

 LINE_CONTAINS phrase one BEFORE {phrase2 AND phrase3} AND LINE_STARTSWITH Therefore we [[['LINE_CONTAINS', [[['phrase', 'one'], 'BEFORE', [['phrase2'], 'AND', ['phrase3']]]]], 'AND', ['LINE_STARTSWITH', [['Therefore', 'we']]]]] [0]: [['LINE_CONTAINS', [[['phrase', 'one'], 'BEFORE', [['phrase2'], 'AND', ['phrase3']]]]], 'AND', ['LINE_STARTSWITH', [['Therefore', 'we']]]] [0]: ['LINE_CONTAINS', [[['phrase', 'one'], 'BEFORE', [['phrase2'], 'AND', ['phrase3']]]]] - line_directive: 'LINE_CONTAINS' - phrase: [[['phrase', 'one'], 'BEFORE', [['phrase2'], 'AND', ['phrase3']]]] [0]: [['phrase', 'one'], 'BEFORE', [['phrase2'], 'AND', ['phrase3']]] [0]: ['phrase', 'one'] [1]: BEFORE [2]: [['phrase2'], 'AND', ['phrase3']] [0]: ['phrase2'] [1]: AND [2]: ['phrase3'] [1]: AND [2]: ['LINE_STARTSWITH', [['Therefore', 'we']]] - line_directive: 'LINE_STARTSWITH' - phrase: [['Therefore', 'we']] [0]: ['Therefore', 'we'] LINE_CONTAINS abcd {upto 4 words} xyzw {upto 3 words} pqrs BEFORE something else [['LINE_CONTAINS', [[['abcd'], ['upto', 4, 'words'], ['xyzw'], ['upto', 3, 'words'], ['pqrs'], 'BEFORE', ['something', 'else']]]]] [0]: ['LINE_CONTAINS', [[['abcd'], ['upto', 4, 'words'], ['xyzw'], ['upto', 3, 'words'], ['pqrs'], 'BEFORE', ['something', 'else']]]] - line_directive: 'LINE_CONTAINS' - phrase: [[['abcd'], ['upto', 4, 'words'], ['xyzw'], ['upto', 3, 'words'], ['pqrs'], 'BEFORE', ['something', 'else']]] [0]: [['abcd'], ['upto', 4, 'words'], ['xyzw'], ['upto', 3, 'words'], ['pqrs'], 'BEFORE', ['something', 'else']] [0]: ['abcd'] [1]: ['upto', 4, 'words'] - numberofwords: 4 [2]: ['xyzw'] [3]: ['upto', 3, 'words'] - numberofwords: 3 [4]: ['pqrs'] [5]: BEFORE [6]: ['something', 'else'] 

Puede iterar sobre estos resultados y separarlos, pero está llegando rápidamente al punto en el que debería ver cómo construir nodos ejecutables a partir de los diferentes niveles de precedencia. Consulte el ejemplo de SimpleBool.py en la wiki de pyparsing para saber cómo hacer esto.

EDITAR: revise esta versión reducida de un analizador para phrase_expr y cómo crea las instancias de Node que generan la salida. Vea cómo se accede a UpToNode en el operador en la clase UpToNode . Vea cómo “xyz abc” se interpreta como “xyz AND abc” con un operador AND implícito.

 from pyparsing import * import re UPTO, WORDS, AND, OR = map(CaselessKeyword, "upto words and or".split()) keyword = UPTO | WORDS | AND | OR LBRACE,RBRACE = map(Suppress, "{}") integer = pyparsing_common.integer() word = ~keyword + Word(alphas) upto_expr = Group(LBRACE + UPTO + integer("numberofwords") + WORDS + RBRACE) class Node(object): def __init__(self, tokens): self.tokens = tokens def generate(self): pass class LiteralNode(Node): def generate(self): return "(%s)" % re.escape(self.tokens[0]) def __repr__(self): return repr(self.tokens[0]) class AndNode(Node): def generate(self): tokens = self.tokens[0] return '.*'.join(t.generate() for t in tokens[::2]) def __repr__(self): return ' AND '.join(repr(t) for t in self.tokens[0].asList()[::2]) class OrNode(Node): def generate(self): tokens = self.tokens[0] return '|'.join(t.generate() for t in tokens[::2]) def __repr__(self): return ' OR '.join(repr(t) for t in self.tokens[0].asList()[::2]) class UpToNode(Node): def generate(self): tokens = self.tokens[0] ret = tokens[0].generate() word_re = r"\s+\S+" space_re = r"\s+" for op, operand in zip(tokens[1::2], tokens[2::2]): # op contains the parsed "upto" expression ret += "((%s){0,%d}%s)" % (word_re, op.numberofwords, space_re) + operand.generate() return ret def __repr__(self): tokens = self.tokens[0] ret = repr(tokens[0]) for op, operand in zip(tokens[1::2], tokens[2::2]): # op contains the parsed "upto" expression ret += " {0-%d WORDS} " % (op.numberofwords) + repr(operand) return ret IMPLICIT_AND = Empty().setParseAction(replaceWith("AND")) phrase_expr = infixNotation(word.setParseAction(LiteralNode), [ (upto_expr, 2, opAssoc.LEFT, UpToNode), (AND | IMPLICIT_AND, 2, opAssoc.LEFT, AndNode), (OR, 2, opAssoc.LEFT, OrNode), ]) tests = """\ xyz xyz abc xyz {upto 4 words} def""".splitlines() for t in tests: t = t.strip() if not t: continue print(t) try: parsed = phrase_expr.parseString(t) except ParseException as pe: print(' '*pe.loc + '^') print(pe) continue print(parsed) print(parsed[0].generate()) print() 

huellas dactilares:

 xyz ['xyz'] (xyz) xyz abc ['xyz' AND 'abc'] (xyz).*(abc) xyz {upto 4 words} def ['xyz' {0-4 WORDS} 'def'] (xyz)((\s+\S+){0,4}\s+)(def) 

Amplíe esto para admitir sus expresiones LINE_xxx .