2012-03-20 18 views
8

Sono nuovo di Scala ma mi stavo chiedendo che è possibile implementare un parser di equazione semplice nella lingua.Scala - Creazione di un parser di funzioni dinamiche di base

Dire che ho un paio di funzioni (molto simile a funzioni di Excel):

IF(Cond a=b, val_true, val_false)

MID(String, Start_pos, num_chars) - stringa estratto

LEN(String) - lunghezza di una stringa

OR(cond1, cond2, ... condn)

AND(cond1, cond2, ... condn)

Quindi l'idea sarebbe ho potuto passare in una formula in fase di esecuzione come una stringa da un utente come un argomento che insieme ad altri params dicono IF(LEN(param1)=4,MID(param1,2,1), MID(param1,0,LEN(param1)))

L'idea è di valutare la funzione, quindi se la l'utente fornisce quella formula sopra e la stringa "scat", quindi l'output sarebbe "a". Se la stringa "scala" è stata data allora l'uscita sarebbe "scala" ...

Quanto sarebbe facile da implementare in Scala? Qual è il miglior approccio al design? So che non ci sono puntatori di funzione (in C avrei analizzato la stringa di formule in una raccolta di punti funzionali e passati da lì) ...

Sarebbe gradito qualsiasi consiglio su come affrontare questo in stile Scala efficiente.

Cheers!

+1

Là _ sono_ l'equivalente dei puntatori di funzione, vale a dire i valori letterali delle funzioni anonime. –

+0

L'idea sarebbe quella di passare la formula e poi fare cosa? Restituisce una funzione che implementa la formula, restituisce un oggetto che rappresenta l'espressione analizzata? – huynhjl

+0

Siamo spiacenti, questo non è abbastanza chiaro. "Potrei passare in una formula": come? dove? in fase di esecuzione? al momento della compilazione? come codice sorgente? come stringa? e ti aspetti che succeda quando lo passi? Se fossi in te, riscriverei di nuovo l'intera domanda. –

risposta

8

Questa domanda ha motivato sperimentare con combinator parsers. Dati i seguenti tipi di dati algebrici che rappresentano un sottoinsieme di espressioni:

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 seguente definizione parser analizzerà la vostra data espressione e restituire un oggetto 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) 
    } 
} 

Poi l'analisi e la valutazione dei risultati possono essere fatto in questo modo:

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") 
} 

Si noti che la grammatica che ho fornito è solo il minimo per far analizzare il tuo esempio e lì assolutamente nessuna gestione degli errori o controllo del tipo. Per quanto riguarda il processo, ho messo a punto una grammatica senza la produzione ^^ ... che analizzerebbe il tuo esempio, quindi ho aggiunto i tipi Expr ma senza il metodo eval, quindi la produzione ^^ ..., infine ho aggiunto i metodi eval al tratto Expr. classi.

+0

Grazie !!! Risposta fantastica! – NightWolf

Problemi correlati