2009-02-08 24 views
11

Mam następujący EBNF że chcę analizować:EBNF Scala parsera combinator

PostfixExp  -> PrimaryExp ("[" Exp "]" 
           | . id "(" ExpList ")" 
           | . length)* 

I to jest to, co mam:

def postfixExp: Parser[Expression] = (
    primaryExp ~ rep(
     "[" ~ expression ~ "]" 
     | "." ~ ident ~"(" ~ repsep(expression, ",") ~ ")" 
     | "." ~ "length") ^^ { 
     case primary ~ list => list.foldLeft(primary)((prim,post) => 
       post match { 
        case "[" ~ length ~ "]" => ElementExpression(prim, length.asInstanceOf[Expression]) 
        case "." ~ function ~"(" ~ arguments ~ ")" => CallMethodExpression(prim, function.asInstanceOf[String], arguments.asInstanceOf[List[Expression]]) 
        case _ => LengthExpression(prim) 
       } 
      ) 
    }) 

Ale chciałbym wiedzieć, czy istnieje lepszy sposób, najlepiej bez uciekania się do rzucania (asInstanceOf).

Odpowiedz

12

zrobiłbym to tak:

type E = Expression 

def postfixExp = primaryExp ~ rep(
    "[" ~> expr <~ "]" ^^ { e => ElementExpression(_:E, e) } 
    | "." ~ "length" ^^^ LengthExpression 
    | "." ~> ident ~ ("(" ~> repsep(expr, ",") <~ ")") ^^ flatten2 { (f, args) => 
     CallMethodExpression(_:E, f, args) 
    } 
) ^^ flatten2 { (e, ls) => collapse(ls)(e) } 

def expr: Parser[E] = ... 

def collapse(ls: List[E=>E])(e: E) = { 
    ls.foldLeft(e) { (e, f) => f(e) } 
} 

expressions skrócona do expr dla zwięzłości, a także dodał typu alias E z tego samego powodu.

Sztuczka, której używam tutaj, aby uniknąć brzydkiej analizy przypadku, to zwrócenie wartości funkcji z wartości wewnętrznej. Ta funkcja pobiera Expression (która będzie primary), a następnie zwraca nową Expression w oparciu o pierwszą. Spowoduje to ujednolicenie dwóch przypadków wysyłania kropek i wyrażeń w nawiasach. Na koniec, metoda collapse jest używana do scalania liniowych List wartości funkcji w odpowiednią AST, zaczynając od określonego pierwotnego wyrażenia.

Należy zwrócić uwagę, że wartość LengthExpression jest zwracana jako wartość (przy użyciu ^^^) z odpowiedniej produkcji. Działa to, ponieważ obiekty towarzyszące dla klas przypadków (zakładając, że LengthExpression jest rzeczywiście klasą przypadku) rozszerzają odpowiednią wartość funkcji delegującą do ich konstruktora. Tak więc funkcja reprezentowana przez LengthExpression przyjmuje pojedynczą wartość Expression i zwraca nową instancję LengthExpression, dokładnie spełniającą nasze potrzeby dotyczące konstrukcji drzewa wyższego rzędu.

Powiązane problemy