2012-03-20 3 views
8

저는 스칼라를 처음 접했지만 간단한 수식 파서를 언어로 구현할 수 있는지 궁금합니다. 문자열 추출스칼라 - 기본 동적 함수 파서 만들기

LEN(String) - - 문자열

OR(cond1, cond2, ... condn)

AND(cond1, cond2, ... condn)의 길이를

IF(Cond a=b, val_true, val_false)

MID(String, Start_pos, num_chars) :

내가 (많은 Excel 함수와 같은) 몇 가지 기능을 가지고 말 그래서 아이디어는 다른 PARAMS는, 그래서 만약 IF(LEN(param1)=4,MID(param1,2,1), MID(param1,0,LEN(param1)))

아이디어는 기능을 평가하는 말을 내가 함께 명령 줄 인수로 사용자로부터 문자열로 런타임에 공식 전달할 수있을 것입니다 사용자가 위의 수식과 문자열 "scat"을 제공하면 결과는 "a"가됩니다. 문자열 "scala"가 주어지면 출력은 "scala"가됩니다 ...

Scala에서 이것을 구현하는 것이 얼마나 쉬운가요? 최고의 디자인 접근법은 무엇입니까? 함수 포인터가 없다는 것을 알았습니다. (C에서 수식 문자열을 함수 점 집합으로 파싱하여 거기에서 빠져 나왔을 것입니다.) ...

효율적인 스칼라 스타일에서이 문제에 접근하는 방법에 대한 조언을 주시면 감사하겠습니다.

건배!

+1

거기에는 함수 포인터와 동등한 익명 함수 리터럴이 있습니다. –

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

내가 제공 한 문법은 예제를 구문 분석하기에 최소값이며 abso라는 점에 유의하십시오. 대충 오류 관리 나 유형 검사가 없습니다. 프로세스를 현명하게하기 위해, 먼저 ^^ ...을 생성하지 않고 문법을 생각해 냈습니다. 그런 다음 Expr 유형을 추가했지만 평가 방법을 사용하지 않은 경우 ^^ ...을 생성 한 다음 Expr 특성 및 하위 유형에 eval 메소드를 추가했습니다. 수업.

+0

감사합니다 !!! 굉장한 대답! – NightWolf