2012-02-11 8 views
9

Estoy escribiendo un analizador para un lenguaje de consulta utilizando PyParsing, y me he quedado atrapado (lo que creo que es) un problema con lookaheads. Un tipo de cláusula en la consulta tiene por objeto dividir cadenas en 3 partes (nombre de campo, operador, valor) de manera que nombre de campo es una palabra, operador es una o más palabras y valor es una palabra, una cadena entre comillas o una lista entre paréntesis de estas.PyParsing lookaheads y expresiones codiciosas

Mis datos parecen

author is william 
author is 'william shakespeare' 
author is not shakespeare 
author is in (william,'the bard',shakespeare) 

Y mi actual del analizador de esta cláusula se escribe como:

fieldname = Word(alphas) 

operator = OneOrMore(Word(alphas)) 

single_value = Word(alphas)^QuotedString(quoteChar="'") 
list_value = Literal("(") + Group(delimitedList(single_value)) + Literal(")") 
value = single_value^list_value 

clause = fieldname + originalTextFor(operator) + value 

Obviamente esto no funciona debido al hecho de que el elemento operator es codicioso y devorará hasta el value si puede. Después de leer otras preguntas similares y los documentos, he llegado a la conclusión de que tengo que gestionar esa anticipación con un NotAny o FollowedBy, pero no he podido encontrar la manera de hacerlo funcionar.

+0

No se pudo que acaba de hacer una lista explícita de posibilidades para los operadores? –

+0

@KarlKnechtel Desafortunadamente, la lista de operadores será extensible. Supongo que podría compilar esa lista en tiempo de ejecución y construir la gramática de forma dinámica, pero parece que sería más limpio poder hacer que el analizador sintiera la agnóstico. Sin embargo, ese es un buen plan de respaldo, en caso de que no pueda resolverlo, así que gracias. –

Respuesta

11

Este es un buen lugar para ser el analizador. O más exactamente, Make The Parser Think Like You Do. Pregúntese: "En 'el autor es Shakespeare', ¿cómo sé que 'Shakespeare' no es parte del operador?" Sabes que 'shakespeare' es el valor porque está al final de la consulta, no hay nada más después. Entonces las palabras del operador no son solo palabras de alphas, son palabras de alfas que no son seguidas por el final de la cadena. Crear ahora que la lógica de búsqueda hacia delante en su definición de operator:

operator = OneOrMore(Word(alphas) + ~FollowedBy(StringEnd())) 

Y creo que esto se iniciará el análisis mejor para usted.

Algunos otros consejos:

  • Sólo uso del operador '^' si habrá alguna ambigüedad posible, como si iba a analizar una cadena con los números que pueden ser números enteros o hexagonal. Si utilicé Word(nums) | Word(hexnums), podría mal procesar "123ABC" como el "123" principal. Al cambiar '|' a '^', se probarán todas las alternativas y se elegirá el partido más largo. En mi ejemplo de analizar números enteros decimales o hexadecimales, podría haber obtenido el mismo resultado al invertir las alternativas, y primero probar para Word(hexnums). En su lenguaje de consulta, no hay forma de confundir una cadena entre comillas con un valor de palabra única no citado (uno lleva ' o ", el otro no), por lo que no hay ninguna razón para usar '^', '| ' Será suficiente. Similar para value = singleValue^listValue.

  • Adición de resultados nombres a los componentes clave de su cadena de consulta hará que sea más fácil trabajar con más adelante:

    clause = fieldname("fieldname") + originalTextFor(operator)("operator") + value("value")

    Ahora puede acceder a los valores analizados por su nombre en lugar de por la posición de análisis (que va ser complicado y propenso a errores, una vez que comienza a recibir más complicado con los campos opcionales y tal):

    queryParts = clause.parseString('author is william')

    print queryParts.fieldname

    print queryParts.operator