2011-05-30 17 views
6

Estoy construyendo un analizador de expresiones del cual me gustaría generar el código de consulta de la base de datos, he llegado bastante lejos pero estoy atascado al analizar BinaryExpressions con precisión. Es bastante fácil dividirlos en Izquierda y Derecha, pero necesito detectar paréntesis y generar mi código en consecuencia y no puedo ver cómo hacerlo.Detectar paréntesis en BinaryExpression

Un ejemplo [por favor, ignora la lógica defectuosa :)]:

a => a.Line2 != "1" && (a.Line2 == "a" || a.Line2 != "b") && !a.Line1.EndsWith("a") 

necesito para detectar el 'conjunto' en el medio y preservar su agrupación, pero no puedo ver ninguna diferencia en la expresión a una normal BinaryExpression durante el análisis (me gustaría comprobar la representación de la cadena para el paréntesis)

Cualquier ayuda sería apreciada.

(probablemente debería mencionar que estoy usando C#)

--Edit-- me olvidó mencionar que estoy usando las clases de expresión .Net estándar para construir las expresiones (System.Linq. expresiones espacio de nombres)

--Edit2-- Ok no estoy de análisis de texto en código, estoy código de análisis en texto. Así que mi clase Parser tiene un método como este:

void FilterWith<T>(Expression<Func<T, bool>> filterExpression); 

que le permite escribir código como este:

FilterWith<Customer>(c => c.Name =="asd" && c.Surname == "qwe"); 

que es bastante fácil de analizar el uso de las clases de .NET estándar, mi reto es analizar esta expresión:

FilterWith<Customer>(c => c.Name == "asd" && (c.Surname == "qwe" && c.Status == 1) && !c.Disabled) 

mi desafío es mantener las expresiones entre paréntesis como un conjunto único. Las clases .Net dividen correctamente las partes entre paréntesis de las otras, pero no da ninguna indicación de que sea un conjunto debido a los paréntesis.

+2

Me limitaré a observar que esto es más difícil de lo que se podría pensar (habiéndolo hecho algunas veces), y es un problema que muchos de los ejemplos de análisis de "juguetes" no abordan convenientemente. –

+1

Los analizadores de voz recursivos son bastante fáciles de poner en marcha, cualquier texto introductorio de nivel universitario sobre la construcción de compiladores lo comentará. Los paréntesis son manejados por la producción expr = "(" + expr + ")". Sin embargo, no es necesario reinventar este tipo de código. Incluso .NET tiene uno: DataTable.Compute(). –

+0

¿Te gustaría compartir lo que has hecho sobre este problema? Acepto, los ejemplos son básicos sobre este tema ... –

Respuesta

6

No he usado Expression, pero si funciona como cualquier otro AST, entonces el problema es más fácil de resolver de lo que imaginas. Como señaló otro comentarista, simplemente ponga paréntesis alrededor de todos de sus expresiones binarias y luego no tendrá que preocuparse por el orden de los problemas de operación.

Alternativamente, podría verificar si la expresión que está generando tiene una precedencia menor que la expresión que lo contiene y, si es así, poner paréntesis alrededor de ella.Por lo tanto, si tiene un árbol como este [* 4 [+ 5 6]] (donde los nodos de los árboles se representan recursivamente como [node left-subtree right-subtree]), al escribir el árbol [+ 4 5] se sabe que estaba dentro de una operación *, que es de mayor precedencia que una operación + y requiere cualquiera de sus subárboles inmediatos se coloca entre paréntesis. El pseudo-código podría ser algo como esto:

function parseBinary(node) { 
    if(node.left.operator.precedence < node.operator.precedence) 
     write "(" + parseBinary(node.left) + ")" 
    else 
     write parseBinary(node.left) 
    write node.operator 
    // and now do the same thing for node.right as you did for node.left above  
} 

Usted tendrá que tener una tabla de precedencia para los distintos operadores, y una manera de conseguir en el propio operador para averiguar lo que es y lo que allí su precedencia es. Sin embargo, imagino que puedes descifrar esa parte.

+0

Por lo que puedo decir, no hay "precedencia". Entonces es MUCHO MÁS fácil analizar recursivamente AndAlso and OrElse y poner paréntesis en todas partes. – Jerther

0

Al construir un analizador de expresiones, primero necesita un analizador, y para eso necesita un tokenizador.

Un tokenizer es una pieza de código que al leer una expresión, genera tokens (que pueden ser válidos o no válidos), para una sintaxis determinada.

Así que su analizador, utilizando el tokenizador, lee la expresión en el orden establecido (de izquierda a derecha, de derecha a izquierda, de arriba a abajo, lo que elija) y crea un árbol que mapea la expresión.

Luego el analizador interpreta el árbol en una expresión, dando su significado definitivo.

+0

mmm, no estoy seguro de entender lo que quieres decir. Estoy usando las clases estándar .Net Expression (System.Linq.Expressions namespace), la que estoy viendo en este momento es BinaryExpression ... –

+2

@Adriaan: Espera, ¿vas de la cadena al árbol de expresiones, o árbol de expresión a la cadena? Si genera una cadena, puede simplemente emitir paréntesis alrededor de cada subexpresión. –

+0

Creo que el ejemplo es árbol de expresión a cadena ... – configurator