2012-03-20 5 views
8

を作成します。スカラ - 私はスカラ座に新たなんだけど、私は言語で簡単な式パーサーを実装することが可能であることを思っていた基本的な動的な機能パーサ

は、私は(多くのExcelの関数のような)いくつかの機能を持っていると言う:

IF(Cond a=b, val_true, val_false)

MID(String, Start_pos, num_chars) - 文字列の長さ

OR(cond1, cond2, ... condn)

AND(cond1, cond2, ... condn) - 文字列が

LEN(String)を抽出

だから、アイデアは、他のparamsと共に、コマンドライン引数は、アイデアが機能を評価することで、その場合IF(LEN(param1)=4,MID(param1,2,1), MID(param1,0,LEN(param1)))

を言うように、私は、ユーザからの文字列として、実行時に式を渡すことができだろうユーザーは上記の式と文字列 "scat"を出力し、出力は "a"になります。文字列「スカラ座」が与えられた場合、出力は「スカラ座」だろう...

どのように簡単にこれはScalaで実装するのだろうか?最高のデザインアプローチは何ですか?私は関数ポインタがないことを知っています(Cでは、式の文字列を関数点の集まりとして解析してそこから抜け出してしまいます)...

効率的なScalaスタイルでこれにアプローチする方法についてのアドバイスをいただければ幸いです。

乾杯!

+1

関数ポインタ、つまり匿名関数リテラルの相当するものはあり_are_。 –

+0

アイデアは、式を渡し、その後、何をするだろうか?式を実装する関数を返し、解析された式を表すオブジェクトを返しますか? – huynhjl

+0

申し訳ありませんが、これは十分明確ではありません。 "私は数式を渡すことができます":どのように?どこ?実行時に?コンパイル時に?ソースコードとして?文字列として?あなたがそれを通過するときに起こることを期待していますか?もし私があなただったら、もう一度全質問を書き直したい。 –

答えて

8

この質問はcombinator parsersを試すことが動機としています。

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 } 

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

をすることができ、あなたの表現のサブセットを表す、次の代数的データ型を考えます

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

私が提供した文法は、あなたの例を解析するのに必要最小限であることに注意してください。エラー管理や型チェックが殆どありません。プロセス賢明では、私が最初にあなたの例を解析します生産^^ ...ずに文法を思い付いた、そして、その後、生産^^ ...、evalメソッドなしExpr種類を追加したが、その後、私は最終的にExpr特性とサブにはevalメソッドを追加しましたクラス。

+0

ありがとう!素晴らしい答え! – NightWolf

関連する問題