2012-03-20 14 views
8

Soy nuevo en Scala pero me preguntaba si es posible implementar un analizador de ecuaciones simple en el idioma.Scala - Creación de un analizador básico de funciones dinámicas

Decir que tengo un par de funciones (tanto como las funciones de Excel):

IF(Cond a=b, val_true, val_false)

MID(String, Start_pos, num_chars) - extracto de cadena

LEN(String) - longitud de una cadena

OR(cond1, cond2, ... condn)

AND(cond1, cond2, ... condn)

Así que la idea sería que pudiera pasar en una fórmula en tiempo de ejecución como una cadena de un usuario como un argumento de línea de comandos junto con cualquier otro params dicen IF(LEN(param1)=4,MID(param1,2,1), MID(param1,0,LEN(param1)))

La idea es evaluar la función, por lo que si el el usuario proporciona esa fórmula anterior y la cadena "scat", entonces la salida sería "a". Si la cadena "scala" fue dada, entonces la salida sería "scala" ...

¿Qué tan fácil sería implementar esto en Scala? ¿Cuál es el mejor enfoque de diseño? Sé que no hay punteros a función (en C habría analizado la cadena de fórmula en una colección de puntos func y luego de allí) ...

Cualquier consejo sobre cómo abordar esto en un estilo eficiente de Scala sería apreciado.

¡Salud!

+1

Hay _are_ el equivalente de punteros a funciones, a saber, literales de funciones anónimas. –

+0

La idea sería pasar la fórmula y luego hacer qué? Devuelve una función que implementa la fórmula, devuelve un objeto que representa la expresión analizada – huynhjl

+0

Lo siento, esto no es lo suficientemente claro. "Podría pasar una fórmula": ¿cómo? ¿dónde? ¿en tiempo de ejecución? en tiempo de compilación? como código fuente? ¿como cuerda? y ¿esperas que suceda cuando lo pases? Si fuera tú, volvería a escribir toda la pregunta. –

Respuesta

8

Esta pregunta ha motivado a experimentar con combinator parsers. Teniendo en cuenta los siguientes tipos de datos algebraicos que representan un subconjunto de sus expresiones:

import scala.util.parsing.combinator._ 
object Expr { type VARS = Map[String, Any] } 
import Expr._ 
sealed trait Expr { def eval(v: VARS) : Any } 

case class If(cond: Cond, ifTrue: Expr, ifFalse: Expr) extends Expr { 
    def eval(v: VARS) = 
    if (cond.eval(v)) ifTrue.eval(v) else ifFalse.eval(v) 
} 
case class Cond(left: Expr, right: Expr) extends Expr { 
    def eval(v: VARS) = left.eval(v) == right.eval(v) 
} 
case class Len(ident: String) extends Expr { 
    def eval(v: VARS) = v(ident).toString.size 
} 
case class Mid(ident: String, start: Expr, count: Expr) extends Expr { 
    def eval(v: VARS) = { 
    val s = start.eval(v).asInstanceOf[Int] 
    val e = s + count.eval(v).asInstanceOf[Int] 
    v(ident).asInstanceOf[String].substring(s, e) 
    } 
} 
case class Ident(ident: String) extends Expr { def eval(v:VARS) = v(ident) } 
case class StringLit(value: String) extends Expr { def eval(v:VARS) = value } 
case class Number(value: String) extends Expr { def eval(v:VARS) = value.toInt } 

La siguiente definición analizador analizará su expresión dada y devolver un objeto Expr:

class Equation extends JavaTokenParsers { 
    def IF: Parser[If] = "IF" ~ "(" ~ booleanExpr ~","~ expr ~","~ expr ~ ")" ^^ { 
    case "IF" ~ "(" ~ booleanExpr ~ "," ~ ifTrue ~ "," ~ ifFalse ~ ")" => 
     If(booleanExpr, ifTrue, ifFalse) 
    } 
    def LEN: Parser[Len] = "LEN" ~> "(" ~> ident <~ ")" ^^ (Len(_)) 
    def MID: Parser[Mid] = "MID" ~ "(" ~ ident ~ "," ~ expr ~ "," ~ expr ~ ")" ^^ { 
    case "MID" ~ "(" ~ ident ~ "," ~ expr1 ~ "," ~ expr2 ~ ")" => 
     Mid(ident, expr1, expr2) 
    } 
    def expr: Parser[Expr] = (
    stringLiteral ^^ (StringLit(_)) 
    | wholeNumber ^^ (Number(_)) 
    | LEN 
    | MID 
    | IF 
    | ident ^^ (Ident(_)) 
) 
    def booleanExpr: Parser[Cond] = expr ~ "=" ~ expr ^^ { 
    case expr1 ~ "=" ~ expr2 => Cond(expr1, expr2) 
    } 
} 

A continuación, el análisis y la evaluación de los resultados se pueden realiza así:

val equation = new Equation 
val parsed = equation.parseAll(equation.expr, 
    """IF(LEN(param1)=4,MID(param1,2,1), MID(param1,0,LEN(param1)))""") 
parsed match { 
    case equation.Success(expr, _) => 
    println(expr) 
    // If(Cond(Len(param1),Number(4)), 
    // Mid(param1,Number(2),Number(1)), 
    // Mid(param1,Number(0),Len(param1))) 
    println(expr.eval(Map("param1" -> "scala"))) // prints scala 
    println(expr.eval(Map("param1" -> "scat"))) // prints a 
    case _ => 
    println("cannot parse") 
} 

Tenga en cuenta que la gramática que he proporcionado es sólo el mínimo para que su ejemplo y no analizar ABSO lutely no hay gestión de errores o verificación de tipos. En cuanto al proceso, primero se me ocurrió una gramática sin la producción ^^ ... que analizaría su ejemplo, luego agregué los tipos Expr pero sin el método eval, luego la producción ^^ ..., luego finalmente agregué los métodos eval al Expr rasgo y sub- clases

+0

Gracias !!! ¡Respuesta impresionante! – NightWolf

Cuestiones relacionadas